[
  {
    "path": ".circleci/circle_T1w.txt",
    "content": ".bids_db\n.bids_db/layout_index.sqlite\n.bidsignore\ndataset_description.json\ngroup_T1w.html\ngroup_T1w.tsv\nlogs\nlogs/config-18480913-163000_PhineasG-ageh-adhi-sacc-ident9b1ab0f.toml\nlogs/mriqc-18480913-163000_PhineasG-ageh-adhi-sacc-ident9b1ab0f.log\nsub-50137\nsub-50137/anat\nsub-50137/anat/sub-50137_T1w.json\nsub-50137/figures\nsub-50137/figures/sub-50137_desc-airmask_T1w.svg\nsub-50137/figures/sub-50137_desc-artifacts_T1w.svg\nsub-50137/figures/sub-50137_desc-background_T1w.svg\nsub-50137/figures/sub-50137_desc-brainmask_T1w.svg\nsub-50137/figures/sub-50137_desc-head_T1w.svg\nsub-50137/figures/sub-50137_desc-noisefit_T1w.svg\nsub-50137/figures/sub-50137_desc-norm_T1w.svg\nsub-50137/figures/sub-50137_desc-segmentation_T1w.svg\nsub-50137/figures/sub-50137_desc-zoomed_T1w.svg\nsub-50137_T1w.html\nsub-50152\nsub-50152/anat\nsub-50152/anat/sub-50152_T1w.json\nsub-50152/figures\nsub-50152/figures/sub-50152_desc-airmask_T1w.svg\nsub-50152/figures/sub-50152_desc-artifacts_T1w.svg\nsub-50152/figures/sub-50152_desc-background_T1w.svg\nsub-50152/figures/sub-50152_desc-brainmask_T1w.svg\nsub-50152/figures/sub-50152_desc-head_T1w.svg\nsub-50152/figures/sub-50152_desc-noisefit_T1w.svg\nsub-50152/figures/sub-50152_desc-norm_T1w.svg\nsub-50152/figures/sub-50152_desc-segmentation_T1w.svg\nsub-50152/figures/sub-50152_desc-zoomed_T1w.svg\nsub-50152_T1w.html\nsub-50785\nsub-50785/anat\nsub-50785/anat/sub-50785_T1w.json\nsub-50785/figures\nsub-50785/figures/sub-50785_desc-airmask_T1w.svg\nsub-50785/figures/sub-50785_desc-artifacts_T1w.svg\nsub-50785/figures/sub-50785_desc-background_T1w.svg\nsub-50785/figures/sub-50785_desc-brainmask_T1w.svg\nsub-50785/figures/sub-50785_desc-head_T1w.svg\nsub-50785/figures/sub-50785_desc-noisefit_T1w.svg\nsub-50785/figures/sub-50785_desc-norm_T1w.svg\nsub-50785/figures/sub-50785_desc-segmentation_T1w.svg\nsub-50785/figures/sub-50785_desc-zoomed_T1w.svg\nsub-50785_T1w.html\nsub-51187\nsub-51187/anat\nsub-51187/anat/sub-51187_T1w.json\nsub-51187/figures\nsub-51187/figures/sub-51187_desc-airmask_T1w.svg\nsub-51187/figures/sub-51187_desc-artifacts_T1w.svg\nsub-51187/figures/sub-51187_desc-background_T1w.svg\nsub-51187/figures/sub-51187_desc-brainmask_T1w.svg\nsub-51187/figures/sub-51187_desc-head_T1w.svg\nsub-51187/figures/sub-51187_desc-noisefit_T1w.svg\nsub-51187/figures/sub-51187_desc-norm_T1w.svg\nsub-51187/figures/sub-51187_desc-segmentation_T1w.svg\nsub-51187/figures/sub-51187_desc-zoomed_T1w.svg\nsub-51187_T1w.html\n/tmp/t1w/derivatives\n"
  },
  {
    "path": ".circleci/circle_bold.txt",
    "content": ".bids_db\n.bids_db/layout_index.sqlite\n.bidsignore\ndataset_description.json\ngroup_bold.html\ngroup_bold.tsv\nlogs\nlogs/config-18480913-163000_PhineasG-ageh-adhi-sacc-ident9b1ab0f.toml\nlogs/mriqc-18480913-163000_PhineasG-ageh-adhi-sacc-ident9b1ab0f.log\nsub-ds205s03\nsub-ds205s03/figures\nsub-ds205s03/figures/sub-ds205s03_task-functionallocalizer_run-01_desc-background_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-functionallocalizer_run-01_desc-brainmask_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-functionallocalizer_run-01_desc-carpet_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-functionallocalizer_run-01_desc-mean_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-functionallocalizer_run-01_desc-norm_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-functionallocalizer_run-01_desc-stdev_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-functionallocalizer_run-01_desc-zoomed_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-01_desc-background_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-01_desc-brainmask_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-01_desc-carpet_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-01_desc-mean_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-01_desc-norm_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-01_desc-stdev_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-01_desc-zoomed_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-02_desc-background_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-02_desc-brainmask_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-02_desc-carpet_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-02_desc-mean_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-02_desc-norm_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-02_desc-stdev_bold.svg\nsub-ds205s03/figures/sub-ds205s03_task-view_run-02_desc-zoomed_bold.svg\nsub-ds205s03/func\nsub-ds205s03/func/sub-ds205s03_task-functionallocalizer_run-01_bold.json\nsub-ds205s03/func/sub-ds205s03_task-functionallocalizer_run-01_timeseries.json\nsub-ds205s03/func/sub-ds205s03_task-functionallocalizer_run-01_timeseries.tsv\nsub-ds205s03/func/sub-ds205s03_task-view_run-01_bold.json\nsub-ds205s03/func/sub-ds205s03_task-view_run-01_timeseries.json\nsub-ds205s03/func/sub-ds205s03_task-view_run-01_timeseries.tsv\nsub-ds205s03/func/sub-ds205s03_task-view_run-02_bold.json\nsub-ds205s03/func/sub-ds205s03_task-view_run-02_timeseries.json\nsub-ds205s03/func/sub-ds205s03_task-view_run-02_timeseries.tsv\nsub-ds205s03_task-functionallocalizer_run-01_bold.html\nsub-ds205s03_task-view_run-01_bold.html\nsub-ds205s03_task-view_run-02_bold.html\nsub-ds205s07\nsub-ds205s07/figures\nsub-ds205s07/figures/sub-ds205s07_task-functionallocalizer_run-01_desc-background_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-functionallocalizer_run-01_desc-brainmask_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-functionallocalizer_run-01_desc-carpet_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-functionallocalizer_run-01_desc-mean_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-functionallocalizer_run-01_desc-norm_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-functionallocalizer_run-01_desc-stdev_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-functionallocalizer_run-01_desc-zoomed_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-01_desc-background_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-01_desc-brainmask_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-01_desc-carpet_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-01_desc-mean_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-01_desc-norm_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-01_desc-stdev_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-01_desc-zoomed_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-02_desc-background_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-02_desc-brainmask_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-02_desc-carpet_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-02_desc-mean_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-02_desc-norm_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-02_desc-stdev_bold.svg\nsub-ds205s07/figures/sub-ds205s07_task-view_run-02_desc-zoomed_bold.svg\nsub-ds205s07/func\nsub-ds205s07/func/sub-ds205s07_task-functionallocalizer_run-01_bold.json\nsub-ds205s07/func/sub-ds205s07_task-functionallocalizer_run-01_timeseries.json\nsub-ds205s07/func/sub-ds205s07_task-functionallocalizer_run-01_timeseries.tsv\nsub-ds205s07/func/sub-ds205s07_task-view_run-01_bold.json\nsub-ds205s07/func/sub-ds205s07_task-view_run-01_timeseries.json\nsub-ds205s07/func/sub-ds205s07_task-view_run-01_timeseries.tsv\nsub-ds205s07/func/sub-ds205s07_task-view_run-02_bold.json\nsub-ds205s07/func/sub-ds205s07_task-view_run-02_timeseries.json\nsub-ds205s07/func/sub-ds205s07_task-view_run-02_timeseries.tsv\nsub-ds205s07_task-functionallocalizer_run-01_bold.html\nsub-ds205s07_task-view_run-01_bold.html\nsub-ds205s07_task-view_run-02_bold.html\nsub-ds205s09\nsub-ds205s09/figures\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-01_desc-background_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-01_desc-brainmask_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-01_desc-carpet_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-01_desc-mean_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-01_desc-norm_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-01_desc-stdev_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-01_desc-zoomed_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-02_desc-background_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-02_desc-brainmask_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-02_desc-carpet_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-02_desc-mean_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-02_desc-norm_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-02_desc-stdev_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-LR_run-02_desc-zoomed_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-RL_run-01_desc-background_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-RL_run-01_desc-brainmask_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-RL_run-01_desc-carpet_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-RL_run-01_desc-mean_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-RL_run-01_desc-norm_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-RL_run-01_desc-stdev_bold.svg\nsub-ds205s09/figures/sub-ds205s09_task-view_acq-RL_run-01_desc-zoomed_bold.svg\nsub-ds205s09/func\nsub-ds205s09/func/sub-ds205s09_task-view_acq-LR_run-01_bold.json\nsub-ds205s09/func/sub-ds205s09_task-view_acq-LR_run-01_timeseries.json\nsub-ds205s09/func/sub-ds205s09_task-view_acq-LR_run-01_timeseries.tsv\nsub-ds205s09/func/sub-ds205s09_task-view_acq-LR_run-02_bold.json\nsub-ds205s09/func/sub-ds205s09_task-view_acq-LR_run-02_timeseries.json\nsub-ds205s09/func/sub-ds205s09_task-view_acq-LR_run-02_timeseries.tsv\nsub-ds205s09/func/sub-ds205s09_task-view_acq-RL_run-01_bold.json\nsub-ds205s09/func/sub-ds205s09_task-view_acq-RL_run-01_timeseries.json\nsub-ds205s09/func/sub-ds205s09_task-view_acq-RL_run-01_timeseries.tsv\nsub-ds205s09_task-view_acq-LR_run-01_bold.html\nsub-ds205s09_task-view_acq-LR_run-02_bold.html\nsub-ds205s09_task-view_acq-RL_run-01_bold.html\n/tmp/bold/derivatives\n"
  },
  {
    "path": ".circleci/config.yml",
    "content": "version: 2.1\norbs:\n  docker: circleci/docker@2.1.4\n\njobs:\n  build:\n    environment:\n      - TZ: \"/usr/share/zoneinfo/America/Los_Angeles\"\n      - MRIQC_API_TAG: 1.1.1\n      - MRIQC_API_DOCKER_IMAGES: \"nginx:1.26.2 swaggerapi/swagger-ui:v5.17.14 mongo:6.0.15\n          python:3.7-slim-bullseye\"\n      - DOCKER_BUILDKIT: 1\n    machine:\n      # https://discuss.circleci.com/t/linux-machine-executor-images-2021-april-q2-update/39928\n      # upgrade Docker version\n      image: default\n      docker_layer_caching: true\n    working_directory: /tmp/src/mriqc\n    steps:\n      - checkout\n      - persist_to_workspace:\n          root: /tmp\n          paths:\n            - src/mriqc\n      - run:\n          name: Check whether build should be skipped\n          command: |\n            if [[ \"$( git log --format='format:%s' -n 1 $CIRCLE_SHA1 | grep -i -E '^docs?(\\(\\w+\\))?:' )\" != \"\" ]]; then\n              echo \"Only docs build\"\n              circleci step halt\n            fi\n      - restore_cache:\n          keys:\n            - build-v4-{{ .Branch }}-{{ .Revision }}\n            - build-v4--{{ .Revision }}\n            - build-v4-{{ .Branch }}-\n            - build-v4-master-\n            - build-v4-\n          paths:\n            - /tmp/docker\n            - /tmp/images\n      - docker/install-docker-credential-helper\n      - run:\n          name: Docker authentication\n          command: |\n            if [[ -n $DOCKER_PAT ]]; then\n              echo \"$DOCKER_PAT\" | docker login -u $DOCKER_USER --password-stdin\n            fi\n      - run:\n          name: Set up Docker registry\n          command: |\n            if [[ -f /tmp/images/registry.tar.gz ]]; then\n              echo \"Loading saved registry image\"\n              docker load < /tmp/images/registry.tar.gz\n            else\n              echo \"Pulling registry image from DockerHub\"\n              docker pull registry:2\n              mkdir -p /tmp/images\n              docker save registry:2 | gzip > /tmp/images/registry.tar.gz\n            fi\n            docker run -d -p 5000:5000 --restart=always --name=registry \\\n                -v /tmp/docker:/var/lib/registry registry:2\n      - run:\n          name: Pull images\n          command: |\n            set +e\n            docker pull localhost:5000/miniconda\n            success=$?\n            set -e\n            if [[ \"$success\" = \"0\" ]]; then\n                echo \"Pulling from local registry\"\n                docker tag localhost:5000/miniconda nipreps/miniconda:py39_2403.0\n                docker pull localhost:5000/mriqc\n                docker tag localhost:5000/mriqc nipreps/mriqc:latest\n                docker tag localhost:5000/mriqc nipreps/mriqc\n            else\n                echo \"Pulling from Docker Hub\"\n                docker pull nipreps/miniconda:py39_2403.0\n                docker tag nipreps/miniconda:py39_2403.0 localhost:5000/miniconda\n                docker push localhost:5000/miniconda\n                docker pull nipreps/mriqc:latest\n            fi\n\n      - run:\n          name: MRIQCWebAPI - Pull Docker images\n          command: |\n            webapi_images=($MRIQC_API_DOCKER_IMAGES)\n            for image in ${webapi_images[@]}; do\n                set +e\n                docker pull localhost:5000/${image}\n                success=$?\n                set -e\n                if [[ \"$success\" = \"0\" ]]; then\n                    docker tag localhost:5000/${image} ${image}\n                else\n                    docker pull ${image}\n                    docker tag ${image} localhost:5000/${image}\n                    docker push localhost:5000/${image}\n                fi\n            done;\n\n      - run:\n          name: Prepare MRIQCWebAPI\n          command: |\n            set +e\n            docker pull localhost:5000/dockereve-master-endpoints:latest\n            success=$?\n            set -e\n            if [[ \"$success\" = 0 ]]; then\n                docker tag localhost:5000/dockereve-master-endpoints:latest dockereve-master-endpoints:latest\n            fi\n\n            rm -rf /tmp/src/mriqcwebapi\n            git clone https://github.com/nipreps/mriqcwebapi.git /tmp/src/mriqcwebapi\n            cd /tmp/src/mriqcwebapi\n            git checkout ${MRIQC_API_TAG}\n            if [ \"${MRIQC_API_SECRET_TOKEN}\" != \"\" ]; then\n              sed -i -E \"s/<secret_token>/$MRIQC_API_SECRET_TOKEN/\" dockereve-master/.env\n              grep -q -i $MRIQC_API_SECRET_TOKEN dockereve-master/.env\n            fi\n            sed -i -E \\\n              -e 's#image: nginx:latest#image: nginx:1.26.2#' \\\n              -e 's#image: swaggerapi/swagger-ui:latest#image: swaggerapi/swagger-ui:v5.17.14#' \\\n              -e 's#image: mongo:latest#image: mongo:6.0.15#' \\\n              dockereve-master/docker-compose.yml\n            docker-compose -f /tmp/src/mriqcwebapi/dockereve-master/docker-compose.yml pull\n            docker-compose -f /tmp/src/mriqcwebapi/dockereve-master/docker-compose.yml build\n\n            docker tag dockereve-master-endpoints:latest localhost:5000/dockereve-master-endpoints:latest\n            docker push localhost:5000/dockereve-master-endpoints:latest\n      - run:\n          name: Build Docker image\n          no_output_timeout: 60m\n          command: |\n            pyenv local 3\n            pip install hatch\n\n            # Get version before making repo dirty\n            THISVERSION=$( hatch version )\n\n            # Inject MRIQC-WebAPI secret\n            if [ \"${MRIQC_API_SECRET_TOKEN}\" != \"\" ]; then\n              sed -i -E \"s/<secret_token>/$MRIQC_API_SECRET_TOKEN/\" mriqc/config.py\n              grep -q -i $MRIQC_API_SECRET_TOKEN mriqc/config.py\n            fi\n\n            if [[ ${THISVERSION:0:1} == \"0\" ]] ; then\n              echo \"WARNING: latest git tag could not be found\"\n              echo \"Please, make sure you fetch all tags from upstream with\"\n              echo \"the command ``git fetch --tags --verbose`` and push\"\n              echo \"them to your fork with ``git push origin --tags``\"\n            fi\n\n            echo \"Building version: $THISVERSION.\"\n\n            # Build docker image\n            e=1 && for i in {1..5}; do\n              docker build \\\n                --cache-from=nipreps/mriqc \\\n                -t nipreps/mriqc:latest \\\n                --build-arg BUILD_DATE=`date -u +\"%Y-%m-%dT%H:%M:%SZ\"` \\\n                --build-arg VCS_REF=`git rev-parse --short HEAD` \\\n                --build-arg VERSION=\"${CIRCLE_TAG:-$THISVERSION}\" . \\\n              && e=0 && break || sleep 15\n            done && [ \"$e\" -eq \"0\" ]\n\n            TARGET_VERSION=\"${CIRCLE_TAG:-$THISVERSION}\"\n            TARGET_VERSION=\"MRIQC v${TARGET_VERSION%+*}\"\n            DOCKER_VERSION=$( docker run --rm nipreps/mriqc:latest --version )\n            DOCKER_VERSION=\"${DOCKER_VERSION%+*}\"\n            echo \"Target version: \\\"${TARGET_VERSION}\\\"\"\n            echo \"Docker version: \\\"${DOCKER_VERSION}\\\"\"\n            test \"${TARGET_VERSION}\" == \"$DOCKER_VERSION\"\n      - run:\n          name: Docker push to local registry\n          no_output_timeout: 40m\n          command: |\n            docker tag nipreps/mriqc:latest localhost:5000/mriqc\n            docker push localhost:5000/mriqc\n            webapi_images=($MRIQC_API_DOCKER_IMAGES)\n            for image in ${webapi_images[@]}; do\n                docker tag ${image} localhost:5000/${image}\n                docker push localhost:5000/${image}\n            done\n      - run:\n          name: Docker registry garbage collection\n          command: |\n            docker exec -it registry /bin/registry garbage-collect --delete-untagged \\\n              /etc/docker/registry/config.yml\n      - save_cache:\n          key: build-v4-{{ .Branch }}-{{ .Revision }}\n          paths:\n            - /tmp/docker\n            - /tmp/images\n      - persist_to_workspace:\n          root: /tmp\n          paths:\n            - src/mriqc\n            - src/mriqcwebapi\n\n  get_data:\n    machine:\n      image: ubuntu-2204:2023.02.1\n    environment:\n      - TZ: \"/usr/share/zoneinfo/America/Los_Angeles\"\n      - TEST_DATA_NAME: \"circle-tests\"\n      - TEST_DATA_URL: \"https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/5b\\\n          6c9950fed49e001a7885b6\"\n    working_directory: /home/circleci/data\n    steps:\n      - checkout:\n          path: /tmp/src/mriqc\n      - run:\n          name: Check whether build should be skipped\n          command: |\n            cd /tmp/src/mriqc\n            if [[ \"$( git log --format='format:%s' -n 1 $CIRCLE_SHA1 | grep -i -E '^docs?(\\(\\w+\\))?:' )\" != \"\" ]]; then\n              echo \"Only docs build\"\n              circleci step halt\n            fi\n      - restore_cache:\n          keys:\n            - data-v2-{{ epoch }}\n            - data-v2-\n      - run:\n          name: Get test data\n          command: |\n            mkdir -p /tmp/data\n            if [[ ! -d /tmp/data/${TEST_DATA_NAME} ]]; then\n              wget --retry-connrefused --waitretry=5 --read-timeout=20 --timeout=15 -t 0 -q \\\n                -O ${TEST_DATA_NAME}.tar.gz \"${TEST_DATA_URL}\"\n              tar xvzf ${TEST_DATA_NAME}.tar.gz -C /tmp/data/\n            else\n              echo \"Dataset ${TEST_DATA_NAME} was cached\"\n            fi\n      - run:\n          name: Create Nipype config files\n          command: |\n            mkdir -p /tmp/t1w /tmp/bold\n            printf \"[execution]\\nstop_on_first_crash = true\\n\" > /tmp/t1w/nipype.cfg\n            echo \"poll_sleep_duration = 0.01\" >> /tmp/t1w/nipype.cfg\n            echo \"hash_method = content\" >> /tmp/t1w/nipype.cfg\n            cp /tmp/t1w/nipype.cfg /tmp/bold/nipype.cfg\n      - save_cache:\n          key: data-v2-{{ epoch }}\n          paths:\n            - /tmp/data\n            - /tmp/t1w\n            - /tmp/bold\n\n  test_pytest:\n    machine:\n      image: ubuntu-2204:2023.02.1\n    working_directory: /home/circleci/out/tests\n    steps:\n      - attach_workspace:\n          at: /tmp\n      - run:\n          name: Check whether build should be skipped\n          command: |\n            cd /tmp/src/mriqc\n            if [[ \"$( git log --format='format:%s' -n 1 $CIRCLE_SHA1 | grep -i -E '^docs?(\\(\\w+\\))?:' )\" != \"\" ]]; then\n              echo \"Only docs build\"\n              circleci step halt\n            fi\n            if [[ \"$( git log --format=oneline -n 1 $CIRCLE_SHA1 | grep -i -E '\\[only[ _]?(anat|func|smoke)\\]' )\" != \"\" ]]; then\n              echo \"Only smoke-tests build\"\n              circleci step halt\n            fi\n      - restore_cache:\n          keys:\n            - build-v4-{{ .Branch }}-{{ .Revision }}\n          paths:\n            - /tmp/docker\n            - /tmp/images\n      - docker/install-docker-credential-helper\n      - run:\n          name: Docker authentication\n          command: |\n            if [[ -n $DOCKER_PAT ]]; then\n              echo \"$DOCKER_PAT\" | docker login -u $DOCKER_USER --password-stdin\n            fi\n      - run:\n          name: Set-up a Docker registry\n          command: |\n            if [[ -f /tmp/images/registry.tar.gz ]]; then\n              echo \"Loading saved registry image\"\n              docker load < /tmp/images/registry.tar.gz\n            else\n              echo \"Pulling registry image from DockerHub\"\n              docker pull registry:2\n            fi\n            docker run -d -p 5000:5000 --restart=always --name=registry \\\n                -v /tmp/docker:/var/lib/registry registry:2\n      - run:\n          name: Pull images from local registry\n          command: |\n            docker pull localhost:5000/mriqc\n            docker tag localhost:5000/mriqc nipreps/mriqc:latest\n      - run:\n          name: Generate _version.py\n          command: |\n            python3 -m pip install build\n\n            pushd /tmp/src/mriqc\n            python3 -m build\n            popd\n      - run:\n          name: Run MRIQC tests\n          no_output_timeout: 2h\n          command: |\n            docker run --rm -ti -v $PWD:/scratch --entrypoint=\"pytest\" \\\n              -v /tmp/src:/src -w /src/mriqc \\\n              nipreps/mriqc:latest mriqc \\\n              --junitxml=/scratch/tests.xml \\\n              --doctest-modules --ignore=mriqc/bin \\\n              --ignore=mriqc/interfaces/transitional.py\n      - store_test_results:\n          path: /home/circleci/out/tests\n\n  T1w:\n    environment:\n      - TZ: \"/usr/share/zoneinfo/America/Los_Angeles\"\n      - TEST_DATA_NAME: \"circle-tests\"\n      - MRIQC_API_DOCKER_IMAGES: \"nginx:1.26.2 swaggerapi/swagger-ui:v5.17.14 mongo:6.0.15\n          python:3.7-slim-bullseye dockereve-master-endpoints:latest\"\n      - MIGAS_OPTOUT: \"1\"\n    machine:\n      # https://discuss.circleci.com/t/linux-machine-executor-images-2021-april-q2-update/39928\n      # upgrade Docker version\n      image: ubuntu-2204:2023.02.1\n    resource_class: large\n    working_directory: /tmp/t1w\n    steps:\n      - attach_workspace:\n          at: /tmp\n      - run:\n          name: Check whether build should be skipped\n          command: |\n            cd /tmp/src/mriqc\n            if [[ \"$( git log --format='format:%s' -n 1 $CIRCLE_SHA1 | grep -i -E '^docs?(\\(\\w+\\))?:' )\" != \"\" ]]; then\n              echo \"Only docs build\"\n              circleci step halt\n            fi\n            if [[ \"$( git log --format=oneline -n 1 $CIRCLE_SHA1 | grep -i -E '\\[only[ _]?func\\]' )\" != \"\" ]]; then\n              echo \"Only functional smoke-tests build\"\n              circleci step halt\n            fi\n      - restore_cache:\n          keys:\n            - build-v4-{{ .Branch }}-{{ .Revision }}\n          paths:\n            - /tmp/docker\n            - /tmp/images\n      - run:\n          name: Set-up a Docker registry\n          command: |\n            if [[ -f /tmp/images/registry.tar.gz ]]; then\n              echo \"Loading saved registry image\"\n              docker load < /tmp/images/registry.tar.gz\n            else\n              echo \"Pulling registry image from DockerHub\"\n              docker pull registry:2\n            fi\n            docker run -d --restart=always --name=registry \\\n                -e REGISTRY_HTTP_ADDR=0.0.0.0:5001 -p 5001:5001 \\\n                -v /tmp/docker:/var/lib/registry registry:2\n      - run:\n          name: Pull images from local registry\n          command: |\n            docker pull localhost:5001/mriqc\n            docker tag localhost:5001/mriqc nipreps/mriqc:latest\n\n            webapi_images=($MRIQC_API_DOCKER_IMAGES)\n            for image in ${webapi_images[@]}; do\n                docker pull localhost:5001/${image}\n                docker tag localhost:5001/${image} ${image}\n            done;\n      - run:\n          name: Start MRIQC WebAPI endpoint\n          command: |\n            docker-compose -f /tmp/src/mriqcwebapi/dockereve-master/docker-compose.yml --verbose up -d\n          background: true\n      - restore_cache:\n          keys:\n            - data-v2-{{ epoch }}\n            - data-v2-\n      - restore_cache:\n          keys:\n            - t1w-v6-{{ .Branch }}\n            - t1w-v6-master\n            - t1w-v6-\n\n      - run:\n          name: Remove old, cached configs\n          command: |\n            rm -f /tmp/t1w/work/.mriqc.*.toml\n            rm -f /tmp/t1w/work/.resources.*.tsv\n            rm -f /tmp/t1w/work/.resources.*.png\n\n      - run:\n          name: Run participant-level on T1w images\n          no_output_timeout: 2h\n          command: |\n            mkdir -p /tmp/t1w/work /tmp/t1w/derivatives\n            # Run MRIQC\n            docker run -u $( id -u ) --rm -ti \\\n                       -v /tmp/data/${TEST_DATA_NAME}:/data:ro \\\n                       -v /tmp/t1w:/scratch -w /scratch \\\n                       -e MRIQC_DEV=1 \\\n                       nipreps/mriqc:latest \\\n                       /data derivatives/ participant \\\n                       -vv --verbose-reports --profile -m T1w --dsname circletests \\\n                       --resource-monitor \\\n                       --n_procs 2 --ants-nthreads 1 --ants-float \\\n                       --webapi-url http://$( hostname -I | awk '{print $1}' )/api/v1 --upload-strict\n\n      - run:\n          name: Move temporary but relevant artifacts\n          command: |\n            mkdir /tmp/t1w/misc\n            mv /tmp/t1w/work/.resources.*.tsv /tmp/t1w/misc\n            mv /tmp/t1w/work/.resources.*.png /tmp/t1w/misc\n\n      - store_artifacts:\n          path: /tmp/t1w/misc\n\n      - save_cache:\n          key: t1w-v6-{{ .Branch }}\n          paths:\n            - /tmp/t1w/work\n\n      - run:\n          name: Run group-level on T1w images\n          no_output_timeout: 2h\n          command: |\n            docker run -u $( id -u ) --rm -ti \\\n                       -v /tmp/data/${TEST_DATA_NAME}:/data:ro \\\n                       -v /tmp/t1w:/scratch -w /scratch \\\n                       -e MRIQC_DEV=1 \\\n                       nipreps/mriqc:latest \\\n                       /data derivatives/ group \\\n                       -m T1w -vv\n\n      - store_artifacts:\n          path: /tmp/t1w/derivatives\n\n      - run:\n          name: Checking presence of outputs\n          command: |\n            mkdir -p /tmp/t1w/test\n            find /tmp/t1w/derivatives | sed s+/tmp/t1w/derivatives/++ | sort > /tmp/t1w/test/outputs.out\n            diff /tmp/src/mriqc/.circleci/circle_T1w.txt /tmp/t1w/test/outputs.out\n            exit $?\n\n      - run:\n          name: Clean-up work directory (just leave reports & commandlines)\n          command: |\n            find /tmp/t1w/work -type f -not -name \"report.rst\" -and -not -name \"command.txt\" -delete\n\n      - store_artifacts:\n          path: /tmp/t1w/work\n\n      - run:\n          name: Checking changes on IQMs\n          command: |\n            docker run --rm -ti -v $PWD:/scratch -w /scratch -v /tmp/src:/src \\\n              --entrypoint=\"dfcheck\" nipreps/mriqc:latest \\\n              -i /scratch/derivatives/group_T1w.tsv \\\n              -r /src/mriqc/mriqc/data/testdata/group_T1w.tsv \\\n            || true  # ignore failure\n\n      - run:\n          name: WebAPI - Check records\n          command: |\n            docker run --rm -ti \\\n              --entrypoint=\"/opt/conda/bin/mriqcwebapi_test\" \\\n              nipreps/mriqc:latest \\\n              T1w 4 \\\n              --webapi-url http://$( hostname -I | awk '{print $1}' )/api/v1/T1w\n\n      - store_artifacts:\n          path: /tmp/t1w/test\n\n  bold:\n    environment:\n      - TZ: \"/usr/share/zoneinfo/America/Los_Angeles\"\n      - TEST_DATA_NAME: \"circle-tests\"\n      - MRIQC_API_DOCKER_IMAGES: \"nginx:1.26.2 swaggerapi/swagger-ui:v5.17.14 mongo:6.0.15\n          python:3.7-slim-bullseye dockereve-master-endpoints:latest\"\n      - MIGAS_OPTOUT: \"1\"\n    machine:\n      # https://discuss.circleci.com/t/linux-machine-executor-images-2021-april-q2-update/39928\n      # upgrade Docker version\n      image: ubuntu-2204:2023.02.1\n    working_directory: /tmp/bold\n    steps:\n      - attach_workspace:\n          at: /tmp\n      - run:\n          name: Check whether build should be skipped\n          command: |\n            cd /tmp/src/mriqc\n            if [[ \"$( git log --format='format:%s' -n 1 $CIRCLE_SHA1 | grep -i -E '^docs?(\\(\\w+\\))?:' )\" != \"\" ]]; then\n              echo \"Only docs build\"\n              circleci step halt\n            fi\n            if [[ \"$( git log --format=oneline -n 1 $CIRCLE_SHA1 | grep -i -E '\\[only[ _]?anat\\]' )\" != \"\" ]]; then\n              echo \"Only anatomical smoke-tests build\"\n              circleci step halt\n            fi\n      - restore_cache:\n          keys:\n            - build-v4-{{ .Branch }}-{{ .Revision }}\n          paths:\n            - /tmp/docker\n            - /tmp/images\n      - run:\n          name: Set-up a Docker registry\n          command: |\n            if [[ -f /tmp/images/registry.tar.gz ]]; then\n              echo \"Loading saved registry image\"\n              docker load < /tmp/images/registry.tar.gz\n            else\n              echo \"Pulling registry image from DockerHub\"\n              docker pull registry:2\n            fi\n            docker run -d --restart=always --name=registry \\\n                -e REGISTRY_HTTP_ADDR=0.0.0.0:5001 -p 5001:5001 \\\n                -v /tmp/docker:/var/lib/registry registry:2\n      - run:\n          name: Pull images from local registry\n          command: |\n            docker pull localhost:5001/mriqc\n            docker tag localhost:5001/mriqc nipreps/mriqc:latest\n\n            webapi_images=($MRIQC_API_DOCKER_IMAGES)\n            for image in ${webapi_images[@]}; do\n                docker pull localhost:5001/${image}\n                docker tag localhost:5001/${image} ${image}\n            done;\n      - run:\n          name: Start MRIQC WebAPI endpoint\n          command: |\n            docker-compose -f /tmp/src/mriqcwebapi/dockereve-master/docker-compose.yml --verbose up -d\n          background: true\n\n      - restore_cache:\n          keys:\n            - data-v2-{{ epoch }}\n            - data-v2-\n      - restore_cache:\n          keys:\n            - bold-v6-{{ .Branch }}\n            - bold-v6-master\n            - bold-v6-\n\n      - run:\n          name: Remove old, cached configs\n          command: |\n            rm -f /tmp/bold/work/.mriqc.*.toml\n            rm -f /tmp/bold/work/.resources.*.tsv\n            rm -f /tmp/bold/work/.resources.*.png\n\n      - run:\n          name: Run participant-level on BOLD images\n          no_output_timeout: 2h\n          command: |\n            mkdir -p /tmp/bold/work /tmp/bold/derivatives\n            # Run MRIQC\n            docker run -u $( id -u ) --rm -ti -v /tmp/data/${TEST_DATA_NAME}:/data:ro \\\n                       -v $PWD:/scratch -w /scratch \\\n                       -e MRIQC_DEV=1 \\\n                       nipreps/mriqc:latest \\\n                       /data derivatives/ participant \\\n                       -vv --verbose-reports --profile -m bold --dsname circletests \\\n                       --n_procs 2 --ants-nthreads 1 --ants-float \\\n                       --resource-monitor --testing \\\n                       --webapi-url http://$( hostname -I | awk '{print $1}' )/api/v1 --upload-strict\n      - run:\n          name: Move temporary but relevant artifacts\n          command: |\n            mkdir /tmp/bold/misc\n            mv /tmp/bold/work/.resources.*.tsv /tmp/bold/misc\n            mv /tmp/bold/work/.resources.*.png /tmp/bold/misc\n\n      - store_artifacts:\n          path: /tmp/bold/misc\n\n      - save_cache:\n          key: bold-v6-{{ .Branch }}\n          paths:\n            - /tmp/bold/work\n\n      - run:\n          name: Run group-level on BOLD images\n          no_output_timeout: 2h\n          command: |\n            docker run -u $( id -u ) --rm -ti -v /tmp/data/${TEST_DATA_NAME}:/data:ro \\\n                       -e MRIQC_DEV=1 \\\n                       -v $PWD:/scratch -w /scratch \\\n                       nipreps/mriqc:latest \\\n                       /data derivatives/ group \\\n                       -m bold -vv\n\n      - store_artifacts:\n          path: /tmp/bold/derivatives\n\n      - run:\n          name: Checking presence of outputs\n          command: |\n            mkdir -p /tmp/bold/test\n            find /tmp/bold/derivatives | sed s+/tmp/bold/derivatives/++ | sort > /tmp/bold/test/outputs.out\n            diff /tmp/src/mriqc/.circleci/circle_bold.txt /tmp/bold/test/outputs.out\n            exit $?\n\n      - run:\n          name: Clean-up work directory (just leave reports & commandlines)\n          command: |\n            find /tmp/bold/work -type f -not -name \"report.rst\" -and -not -name \"command.txt\" -delete\n\n      - store_artifacts:\n          path: /tmp/bold/work\n\n      - run:\n          name: Checking changes on IQMs\n          command: |\n            docker run -u $( id -u ) --rm -ti -v /tmp/src:/src -v $PWD:/scratch -w /scratch \\\n              --entrypoint=\"dfcheck\" nipreps/mriqc:latest \\\n              -i /scratch/derivatives/group_bold.tsv \\\n              -r /src/mriqc/mriqc/data/testdata/group_bold.tsv\n\n      - run:\n          name: WebAPI - Check records\n          command: |\n            docker run --rm -ti \\\n              --entrypoint=\"/opt/conda/bin/mriqcwebapi_test\" \\\n              nipreps/mriqc:latest \\\n              bold 9 \\\n              --webapi-url http://$( hostname -I | awk '{print $1}' )/api/v1/bold\n\n      - store_artifacts:\n          path: /tmp/bold/test\n    # The resource_class feature allows configuring CPU and RAM resources for each job. Different resource classes are available for different executors. https://circleci.com/docs/2.0/configuration-reference/#resourceclass\n    resource_class: large\n\n  build_docs:\n    docker:\n      - image: cimg/python:3.10\n    environment:\n      - FSLOUTPUTTYPE: NIFTI\n    steps:\n      - checkout\n      - run:\n          name: Install Graphviz\n          command: sudo apt update && sudo apt -y install graphviz\n      - run:\n          name: Install deps\n          command: |\n            pip install -U pip hatch docutils\n            hatch version\n            pip install .[docs]\n      - run:\n          name: Build MRIQC documentation\n          no_output_timeout: 2h\n          command: |\n            make -C docs SPHINXOPTS=\"-W\" html | tee $PWD/builddocs.log\n            cat $PWD/builddocs.log\n            grep -qv \"ERROR\" $PWD/builddocs.log\n      - store_artifacts:\n          path: ./docs/_build/html\n\n  test_package:\n    docker:\n      - image: cimg/python:3.9\n    working_directory: /tmp/src/mriqc\n    steps:\n      - checkout\n      - run:\n          name: Start virtual environment\n          command: |\n            python -m venv /tmp/venv\n            source /tmp/venv/bin/activate\n            python -m pip install -U build hatch hatchling pip twine docutils\n      - run:\n          name: Build and check\n          command: |\n            source /tmp/venv/bin/activate\n            python -m build -s -w\n            python -m twine check dist/*\n      - run:\n          name: Validate version\n          command: |\n            source /tmp/venv/bin/activate\n            THISVERSION=$( python3 -m hatch version | tail -n1 | xargs )\n            python -m pip install dist/*.tar.gz\n            mkdir empty\n            cd empty\n            INSTALLED=$( python -c 'import mriqc; print(mriqc.__version__)' )\n            test \"${CIRCLE_TAG:-$THISVERSION}\" == \"$INSTALLED\"\n\n  deploy_pypi:\n    docker:\n      - image: cimg/python:3.10\n    working_directory: /tmp/src/mriqc\n    steps:\n      - checkout\n      - run:\n          name: Start virtual environment\n          command: |\n            python -m venv /tmp/venv\n            source /tmp/venv/bin/activate\n            python -m pip install -U hatch hatchling pip build twine docutils\n      - run:\n          name: Deploy to PyPi\n          command: |\n            source /tmp/venv/bin/activate\n\n            # Set version on stone before editing the bundle\n            export SETUPTOOLS_SCM_PRETEND_VERSION=$( python -m hatch version | tail -n1 | xargs )\n\n            # Inject MRIQC-WebAPI secret\n            if [ \"${MRIQC_API_SECRET_TOKEN}\" != \"\" ]; then\n              sed -i -E \"s/<secret_token>/$MRIQC_API_SECRET_TOKEN/\" mriqc/config.py\n              grep -q -i $MRIQC_API_SECRET_TOKEN mriqc/config.py\n            fi\n            python -m build -s -w\n            python -m twine check dist/*\n            python -m twine upload dist/*\n\n  deploy_docker:\n    machine:\n      image: default\n    working_directory: /tmp/src/mriqc\n    steps:\n      - restore_cache:\n          keys:\n            - build-v4-{{ .Branch }}-{{ .Revision }}\n          paths:\n            - /tmp/docker\n            - /tmp/images\n      - docker/install-docker-credential-helper\n      - run:\n          name: Docker authentication\n          command: |\n            if [[ -n $DOCKER_PAT ]]; then\n              echo \"$DOCKER_PAT\" | docker login -u $DOCKER_USER --password-stdin\n            fi\n      - run:\n          name: Set-up a Docker registry\n          command: |\n            if [[ -f /tmp/images/registry.tar.gz ]]; then\n              echo \"Loading saved registry image\"\n              docker load < /tmp/images/registry.tar.gz\n            else\n              echo \"Pulling registry image from DockerHub\"\n              docker pull registry:2\n            fi\n            docker run -d -p 5000:5000 --restart=always --name=registry \\\n                -v /tmp/docker:/var/lib/registry registry:2\n      - run:\n          name: Pull images from local registry\n          command: |\n            docker pull localhost:5000/mriqc\n            docker tag localhost:5000/mriqc nipreps/mriqc:latest\n\n      - run:\n          name: Deploy to Docker Hub\n          no_output_timeout: 40m\n          command: |\n            # only tag & push latest if CIRCLE_TAG is set\n            if [ -n \"${CIRCLE_TAG:-}\" ]; then\n              docker push nipreps/mriqc:latest\n            fi\n\n            docker tag nipreps/mriqc nipreps/mriqc:\"${CIRCLE_TAG:=experimental}\"\n            docker push nipreps/mriqc:\"$CIRCLE_TAG\"\n            echo \"Pushed tag ${CIRCLE_TAG} to Docker Hub\"\n\nworkflows:\n  version: 2\n  build_test_deploy:\n    jobs:\n      - build_docs:\n          filters:\n            tags:\n              only: /.*/\n\n      - build:\n          context:\n            - nipreps-common\n          filters:\n            branches:\n              ignore: /docs?\\/.*/\n            tags:\n              only: /.*/\n\n      - get_data:\n          filters:\n            branches:\n              ignore: /docs?\\/.*/\n            tags:\n              only: /.*/\n\n      - test_package:\n          context:\n            - nipreps-common\n          filters:\n            branches:\n              ignore: /docs?\\/.*/\n            tags:\n              only: /.*/\n\n      - test_pytest:\n          context:\n            - nipreps-common\n          requires:\n            - build\n          filters:\n            branches:\n              ignore: /docs?\\/.*/\n            tags:\n              only: /.*/\n\n      - T1w:\n          requires:\n            - get_data\n            - build\n          filters:\n            branches:\n              ignore: /docs?\\/.*/\n            tags:\n              only: /.*/\n\n      - bold:\n          requires:\n            - get_data\n            - build\n          filters:\n            branches:\n              ignore: /docs?\\/.*/\n            tags:\n              only: /.*/\n\n      - deploy_docker:\n          context:\n            - nipreps-common\n          requires:\n            - build\n            - test_pytest\n            - test_package\n            - build_docs\n            - T1w\n            - bold\n          filters:\n            branches:\n              only:\n                - master\n            tags:\n              only: /.*/\n\n      - deploy_pypi:\n          context:\n            - nipreps-common\n          requires:\n            - deploy_docker\n          filters:\n            branches:\n              ignore: /.*/\n            tags:\n              only: /.*/\n"
  },
  {
    "path": ".codecov.yml",
    "content": "codecov:\n  token: 507d37e9-5aac-4c1e-b28c-f2b4ef9d2edf\n  branch: master\n  bot: oesteban\n\ncoverage:\n  precision: 2\n  round: down\n  range: \"70...100\"\n\n  status:\n    project:\n      default:\n        target: auto\n        threshold: 5.0\n        branches:\n          - master\n\n    patch:\n      default:\n        target: auto\n        branches:\n          - master\n\n    changes:\n      default:\n        branches:\n          - master\n\n  ignore:\n    - build/.*\n    - agave/.*\n    - .*/data/.*\n\ncomment:\n  layout: \"header, diff, changes, sunburst, uncovered\"\n  branches:\n    - master\n  behavior: default\n"
  },
  {
    "path": ".dockerignore",
    "content": "# python cache\n.cache/\n__pycache__/**/*\n__pycache__\n*.pyc\n\n# python distribution\nbuild/**/*\nbuild\ndist/**/*\ndist\nmriqc.egg-info/**/*\nmriqc.egg-info\n.eggs/**/*\n.eggs\nsrc/**/*\nsrc/\nnotebooks/\n.maint/\n.maint/**/*\n\n# releasing\nMakefile\n\n###\n### git MUST NOT be ignored after moving to multi-staged builds\n###\n### with the multi-staged build, the strategy is to create a wheel\n### in a stage image. To create such a wheel, the git repo is necessary\n### for hatch-vcs to interpolate the version and initialize _version.py\n###\n# .github/\n# .gitignore\n# .gitattributes\n# .git/**/*\n# .git\n\n# Maintenance\n.circleci/\n.maint/\n.codecov.yml\n.coveragerc\n.pep8speaks.yml\n.pylintrc\n.readthedocs.yaml\n.travis.yml\n.zenodo.json\nCONTRIBUTING.md\ncodecov.yml\ndocs\ndocs/**/*\nlong_description.rst\nnotebooks\nnotebooks/**/*\nrequirements-dev.txt\nrequirements.txt\nvenv/\nvenv/**/*\n"
  },
  {
    "path": ".git_archival.txt",
    "content": "node: $Format:%H$\nnode-date: $Format:%cI$\ndescribe-name: $Format:%(describe:tags=true,match=*[0-9]*)$\nref-names: $Format:%D$\n"
  },
  {
    "path": ".gitattributes",
    "content": ".git_archival.txt  export-subst\n"
  },
  {
    "path": ".github/config.yml",
    "content": "# Comment to be posted to on pull requests merged by a first time user\nfirstPRMergeComment: >\n  Thanks for opening this pull request and congratulations for taking it to the finish line!\n  It looks like this is your first time contributing to *MRIQC*. :smile:\n  \n  We invite you to list yourself as an *MRIQC* contributor.\n  To learn more about what that entails and how we credit our contributors,\n  please check out the\n  [contributing guidelines](https://www.nipreps.org/community/CONTRIBUTING/#recognizing-contributions).\n  If your name is not already on the list, please insert it, in alphabetical order of (i) lastname and\n  (ii) firstname, into the\n  [``.maint/CONTRIBUTORS.md`` file](https://github.com/nipreps/mriqc/blob/master/.maint/CONTRIBUTORS.md).\n\n  Of course, if you want to opt-out this time, there is no\n  problem at all with adding your name later.\n  You will be always welcome to add it in the future whenever\n  you feel it should be listed.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "---\nversion: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/pythonpackage.yml",
    "content": "# This workflow will install Python dependencies, run tests and lint with a variety of Python versions\n# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions\n\nname: Python package\n\non:\n  push:\n    branches: [ '*' ]\n    tags: [ '*' ]\n  pull_request:\n    branches: [ master, 'maint/*' ]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    if: \"!startsWith(github.ref, 'refs/tags/') && !contains(github.event.head_commit.message, '[skip ci]')\"\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']\n\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        fetch-depth: 0\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v6\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Build and check package\n      run: |\n        pipx run build\n        pipx run twine check dist/mriqc-*\n    - name: Interpolate version\n      run: |\n        # Interpolate version\n        if [[ \"$GITHUB_REF\" == refs/tags/* ]]; then\n          TAG=${GITHUB_REF##*/}\n        fi\n        THISVERSION=$( pipx run hatch version | tail -n1 | xargs )\n        THISVERSION=${TAG:-$THISVERSION}\n        echo \"Expected VERSION: \\\"${THISVERSION}\\\"\"\n        echo \"THISVERSION=${THISVERSION}\" >> $GITHUB_ENV\n    - name: Install in confined environment [pip]\n      run: |\n        python -m venv /tmp/pip\n        source /tmp/pip/bin/activate\n        python -m pip install .\n        INSTALLED_VERSION=$(python -c 'import mriqc as qc; print(qc.__version__, end=\"\")')\n        echo \"INSTALLED: \\\"${INSTALLED_VERSION}\\\"\"\n        test \"${INSTALLED_VERSION}\" = \"${THISVERSION}\"\n        rm -r /tmp/pip\n    - name: Install in confined environment [sdist]\n      run: |\n        python -m venv /tmp/install_sdist\n        source /tmp/install_sdist/bin/activate\n        python -m pip install dist/mriqc*.tar.gz\n        INSTALLED_VERSION=$(python -c 'import mriqc as qc; print(qc.__version__, end=\"\")')\n        echo \"INSTALLED: \\\"${INSTALLED_VERSION}\\\"\"\n        test \"${INSTALLED_VERSION}\" = \"${THISVERSION}\"\n        rm -r /tmp/install_sdist\n    - name: Install in confined environment [wheel]\n      run: |\n        python -m venv /tmp/install_wheel\n        source /tmp/install_wheel/bin/activate\n        python -m pip install dist/mriqc*.whl\n        INSTALLED_VERSION=$(python -c 'import mriqc as qc; print(qc.__version__, end=\"\")')\n        echo \"INSTALLED: \\\"${INSTALLED_VERSION}\\\"\"\n        test \"${INSTALLED_VERSION}\" = \"${THISVERSION}\"\n        rm -r /tmp/install_wheel\n\n  ruff:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n    - run: |\n        pipx run ruff check --output-format=github\n        pipx run ruff format --diff\n\n  codespell:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n    - uses: codespell-project/actions-codespell@v2\n"
  },
  {
    "path": ".gitignore",
    "content": "# setuptools-scm\nmriqc/_version.py\n\n.DS_Store\n\n# IDE configuration\n.idea/\n.vscode\n\n# Documentation build\ndocs/build\nbuild/\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nvenv/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff\n*.log\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Run files\nwork/"
  },
  {
    "path": ".mailmap",
    "content": "Aaron Piccirilli <apiccirilli@gmail.com>\nAdam Huffman <bloch@verdurin.com>\nAdam Huffman <bloch@verdurin.com> verdurin\nAdam C. Raikes <adam.raikes@gmail.com>\nAdam C. Raikes <adam.raikes@gmail.com> araikes\nAdam C. Raikes <adam.raikes@gmail.com> Adam Raikes\nAriel Rokem <arokem@gmail.com>\nAsier Erramuzpe <asier.erramuzpe@gmail.com>\nAsier Erramuzpe <asier.erramuzpe@gmail.com> erramuzpe\nBennet Fauber <justbennet@users.noreply.github.com>\nBennet Fauber <justbennet@users.noreply.github.com> justbennet\nCéline Provins <celine.provins@unil.ch>\nCéline Provins <celine.provins@unil.ch> cprovins\nCéline Provins <celine.provins@unil.ch> celprov\nCéline Provins <celine.provins@unil.ch> <77437752+celprov@users.noreply.github.com>\nChristopher J. Markiewicz <effigies@gmail.com>\nChristopher J. Markiewicz <effigies@gmail.com> <effigies@bu.edu>\nChristopher J. Markiewicz <effigies@gmail.com> <markiewicz@stanford.edu>\nConrad Ma <Conrad@Conrads-MacBook-Pro.local>\nConrad Ma <Conrad@Conrads-MacBook-Pro.local> Conrad \nConrad Ma <Conrad@Conrads-MacBook-Pro.local> 394822740 \nConrad Ma <Conrad@Conrads-MacBook-Pro.local> <394822740@qq.com>\nConrad Ma <Conrad@Conrads-MacBook-Pro.local> <maconrad49@gmail.com>\ndependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nDimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com>\nDylan Nielson <adenosine@gmail.com>\nDylan Nielson <adenosine@gmail.com> Dylan <Dylan.Nielson@nih.gov>\nElodie Savary <elodie.savary@outlook.com>\nElodie Savary <elodie.savary@outlook.com> esavary\nJames D. Kent <james-kent@uiowa.edu>\nJames D. Kent <james-kent@uiowa.edu> <jamesdkent21@gmail.com>\nJames D. Kent <james-kent@uiowa.edu> jdkent\nJames D. Kent <james-kent@uiowa.edu> Fred Mertz <mertzf@bargle.argle>\nJohannes Achtzehn <johannes.achtzehn@gmail.com>\nJohannes Achtzehn <johannes.achtzehn@gmail.com> jAchtzehn\nJohn A. Lee <johnleenimh@gmail.com>\nJohn A. Lee <johnleenimh@gmail.com> john anthony lee\nJohn A. Lee <johnleenimh@gmail.com> leej3\nKrzysztof J. Gorgolewski <krzysztof.gorgolewski@gmail.com>\nKrzysztof J. Gorgolewski <krzysztof.gorgolewski@gmail.com> <chris.gorgolewski@gmail.com>\nKrzysztof J. Gorgolewski <krzysztof.gorgolewski@gmail.com> <filo@filo-dtc-laptop>\nKrzysztof J. Gorgolewski <krzysztof.gorgolewski@gmail.com> <filo@filo-Precision-M6500.(none)>\nKrzysztof J. Gorgolewski <krzysztof.gorgolewski@gmail.com> <filo@filo-Precision-M6500>\nKrzysztof J. Gorgolewski <krzysztof.gorgolewski@gmail.com> <filo@filolaptop>\nKrzysztof J. Gorgolewski <krzysztof.gorgolewski@gmail.com> <krzysztof.gorgolewski+fake@gmail.com>\nMathias Goncalves <goncalves.mathias@gmail.com>\nMathias Goncalves <goncalves.mathias@gmail.com> mathiasg\nMathias Goncalves <goncalves.mathias@gmail.com> <mathiasg@mit.edu>\nMathias Goncalves <goncalves.mathias@gmail.com> <mathiasg@stanford.edu>\nMcKenzie P. Hagen <mphagen@uw.edu>\nMcKenzie P. Hagen <mphagen@uw.edu> mckenziephagen\nMichael Dayan <michael.dayan@fcbg.ch>\nMichael G. Clark <michael.clark3@nih.gov>\nMichael G. Clark <michael.clark3@nih.gov> mgclark\nMichael G. Clark <michael.clark3@nih.gov> Michael Clark\nMichael Krause <krause@mpib-berlin.mpg.de>\nMichael Krause <krause@mpib-berlin.mpg.de> octomike\nNiPreps Bot <nipreps@gmail.com>\nNiPreps Bot <nipreps@gmail.com> nipreps-bot\nPablo Velasco <pablo.velasco@nyu.edu>\nPablo Velasco <pablo.velasco@nyu.edu> pvelasco\nPatrick Sadil <psadil@gmail.com>\nPatrick Sadil <psadil@gmail.com> psadil\nOscar Esteban <code@oscaresteban.es>\nOscar Esteban <code@oscaresteban.es> <oesteban@stanford.edu>\nRoss Blair <rblair2@stanford.edu>\nRoss Blair <rblair2@stanford.edu> <rosswilsonblair@gmail.com>\nTeresa Gomez <46339554+teresamg@users.noreply.github.com>\nTeresa Gomez <46339554+teresamg@users.noreply.github.com> teresamg\nYibei Chen <yibeichan@gmail.com>\nVictor Férat <victor.ferat@live.Fr>\nVictor Férat <victor.ferat@live.Fr> vferat\nWilliam Triplett <wtriplet@stanford.edu>\nZvi Baratz <z.baratz@gmail.com>\nZvi Baratz <z.baratz@gmail.com> ZviBaratz\nZvi Baratz <z.baratz@gmail.com> zvi-quantivly\nZvi Baratz <z.baratz@gmail.com> <133042121+zvi-quantivly@users.noreply.github.com>\nZvi Baratz <z.baratz@gmail.com> <zvi.baratz@quantivly.com>\n"
  },
  {
    "path": ".maint/CONTRIBUTORS.md",
    "content": "# CONTRIBUTORS\n\nThis document lists those who have made contributions to the Project.\nAs per the contributor guidelines, they should be included by default in any publications derived from the Project.\n\nIf you are new to the project, don't forget to add your name and affiliation to the list of contributors here! Our Welcome Bot will send an automated message reminding this to first-time contributors.\nBefore every release, unlisted contributors will be invited again to add their names to this file (just in case they missed the automated message from our Welcome Bot).\n\n| **Lastname** | **Name** | **Handle** | **ORCID** | **Affiliation** |\n| --- | --- | --- | --- | --- |\n| Achtzehn | Johannes | @jAchtzehn | 0000-0002-0657-6516 | Charité Berlin, Berlin, Germany |\n| Baratz | Zvi | @zvi-quantivly | 0000-0001-7159-1387 | Quantivly Inc., Somerville, MA, USA |\n| Beliy | Nikita | @nbeliy | | CRC ULiege, Liege, Belgium |\n| Birman | Daniel | @dbirman | 0000-0003-3748-6289 | Department of Psychology, Stanford University, CA, USA |\n| Blair | Ross W. | @rwblair | 0000-0003-3007-1056 | Department of Psychology, Stanford University, CA, USA |\n| Chen | Yibei | @yibeichan | 0000-0003-2882-0900 | McGovern Institute for Brain Research, Massachusetts Institute of Technology, Cambridge, USA |\n| Clark | Michael G. | @mgclark | | National Institutes of Health, USA |\n| Dayan | Michael | @neurorepro | 0000-0002-2666-0969 | International Committee of the Red Cross - ICRC, Geneva, Switzerland |\n| Durnez | Joke | @jokedurnez | 0000-0001-9030-2202 | Department of Psychology, Stanford University, CA, USA |\n| Erramuzpe | Asier | @erramuzpe | 0000-0002-9402-2184 | Computational Neuroimaging Lab, BioCruces Health Research Institute |\n| Fauber | Bennet | @justbennet | | University of Michigan, Ann Arbor, USA |\n| Férat | Victor | @vferat | 0000-0003-1952-7657 | Department of Basic Neurosciences, Université de Genève, Geneva, Switzerland |\n| Garcia-Dias | Rafael | @garciadias | 0000-0001-9332-1580 | Institute of Psychiatry, Psychology & Neuroscience, King's College London, London, UK |\n| Ghosh | Satrajit S. | @satra | 0000-0002-5312-6729 | McGovern Institute for Brain Research, MIT, MA, USA; and Department of Otolaryngology, Harvard Medical School, MA, USA |\n| Gomez | Teresa | @teresamg | | The University of Washington eScience Institute, WA, USA |\n| Goncalves | Mathias | @mgxd | 0000-0002-7252-7771 | Department of Psychology, Stanford University, CA, USA |\n| Halchenko | Yaroslav O. | @yarikoptic | 0000-0003-3456-2493 | Psychological and Brain Sciences Department, Dartmouth College, NH, USA |\n| Huffman | Adam | @verdurin | | Department of Physics, Imperial College London, London, UK |\n| Kay | Benjamin | @benkay86 | | Washington University School of Medicine, St.Louis, MO, USA |\n| Kent | James D. | @jdkent | 0000-0002-4892-2659 | Department of Psychology, University of Texas at Austin, TX, USA |\n| Krause | Michael | @octomike | | Max Planck Institute for Human Development, Berlin, Germany |\n| Lee | John A. | @leej3 | 0000-0001-5884-4247 | Quansight, Dublin, Ireland |\n| Legarreta Gorroño | Jon Haitz | @jhlegarreta | 0000-0002-9661-1396 | Brigham and Women's Hospital, Mass General Brigham, Harvard Medical School, MA, USA |\n| Nichols | Thomas | @nicholst | 0000-0002-4516-5103 | Oxford Big Data Institute, University of Oxford, Oxford, GB |\n| Nielson | Dylan | @Shotgunosine | 0000-0003-4613-6643 | Section on Clinical and Computational Psychiatry, National Institute of Mental Health, Bethesda, MD, USA |\n| Papadopoulos Orfanos | Dimitri | @DimitriPapadopoulos | 0000-0002-1242-8990 | NeuroSpin, CEA, Université Paris-Saclay, NeuroSpin, Gif-sur-Yvette, France |\n| Raikes | Adam C. | @araikes | | Center for Innovation in Brain Science, University of Arizona, Tucson, AZ, USA |\n| Rokem | Ariel | @arokem | 0000-0003-0679-1985 | The University of Washington eScience Institute, WA, USA |\n| Sadil | Patrick | @psadil | 0000-0003-4141-1343 | Johns Hopkins Bloomberg School of Public Health, MD, USA |\n| Salo | Taylor | @tsalo | 0000-0001-9813-3167 | Department of Psychology, Florida International University, FL, USA |\n| Savary | Elodie | @esavary | 0000-0002-3896-6906 | Department of Radiology, Lausanne University Hospital and University of Lausanne, Switzerland |\n| Tooley | Ursula A. | @utooley | 0000-0001-6377-3885 | Department of Neuroscience, University of Pennsylvania, PA, USA |\n| Triplett | William | @wtriplett | 0000-0002-9546-1306 | University of Florida: Gainesville, Florida, US |\n| Varada | Jan | @jvarada | | Functional MRI Facility, National Institute of Mental Health, Bethesda, MD, USA |\n| Velasco | Pablo | @pvelasco | 0000-0002-5749-6049 | Center for Brain Imaging, New York University, NY, USA |\n"
  },
  {
    "path": ".maint/FORMER.md",
    "content": "# FORMER MEMBERS\n\nThis document lists former contributors or maintainers who want to disengage from the Project, and seek to be dismissed in communications or future papers.\nBy adding your name to this list you are giving up on all your responsibilities to the project.\nShould you desire to be considered back as a contributor or maintainer, please remove your name from this list and proceed as prescribed in the governance documents.\n\n| **Lastname** | **Name** | **Handle** |\n| --- | --- | --- |\n| Bot | NiPreps | @nipreps-bot |\n| dependabot[bot] | | @dependabot |\n| Ma | Conrad | @394822740 |\n| Piccirilli | Aaron | @apiccirilli |\n| Gorgolewski | Krzysztof J. | @chrisgorgo |\n"
  },
  {
    "path": ".maint/MAINTAINERS.md",
    "content": "# Maintainers\n\nThis document lists the Maintainers of the project.\nMaintainers may be added once approved by the existing maintainers as described in the `../GOVERNANCE.md` document.\nBy adding your name to this list you are agreeing to abide by the governance documents and to abide by all of the Organization's polices, including the code of conduct, trademark policy, and antitrust policy.\nIf you are participating because of your affiliation with another organization (designated below), you represent that you have the authority to bind that organization to these policies.\n\n<!-- EXAMPLE: The current contents of the table are given for an example, please update. -->\n\n| **Lastname** | **Name** | **Handle** | **ORCID** | **Affiliation** |\n| --- | --- | --- | --- | --- |\n| Provins | Céline | @celprov | 0000-0002-1668-9629 | Department of Radiology, Lausanne University Hospital and University of Lausanne, Switzerland |\n| Hagen | McKenzie P. | @mckenziephagen | 0000-0002-7454-8189 | Psychology Department, University of Washington, Seattle, WA, USA |\n| MacNicol | Eilidh | @eilidhmacnicol | 0000-0003-3715-7012 | Department of Neuroimaging, Institute of Psychiatry, Psychology and Neuroscience, King's College London, London, UK |\n| Esteban | Oscar | @oesteban | 0000-0001-8435-6191 | Lausanne University Hospital and University of Lausanne, Lausanne, Switzerland |\n| Markiewicz | Christopher J. | @effigies | 0000-0002-6533-164X | Department of Psychology, Stanford University, CA, USA |\n"
  },
  {
    "path": ".maint/PIs.md",
    "content": "# PRINCIPAL INVESTIGATORS\n\nThis documents the key personnel who oversees the development of the Project and secures funding.\nThe names in this file are designated by the Organization's TSC at the time of accepting the Project under the Organization.\nChanges to this file must be approved by the TSC.\nWhen a PI ceases serving as such, by default, their name will be placed in the `CONTRIBUTORS.md` file should it not be there already.\n\nBy having your name in this list you are agreeing to abide by the governance documents and to abide by all of the Organization's polices, including the code of conduct, trademark policy, and antitrust policy.\nIf you are participating because of your affiliation with another organization (designated below), you represent that you have the authority to bind that organization to these policies.\n\n| **Lastname** | **Name** | **Handle** | **ORCID** | **Affiliation** |\n| --- | --- | --- | --- | --- |\n| Esteban | Oscar | @oesteban | 0000-0001-8435-6191 | Department of Radiology, Lausanne University Hospital and University of Lausanne, Switzerland |\n| Rokem | Ariel | @arokem | 0000-0003-0679-1985 | The University of Washington eScience Institute, WA, USA |\n| Poldrack | Russell A. | @poldrack | 0000-0001-6755-0259 | Department of Psychology, Stanford University, CA, USA |\n| Thomas | Adam G. | | 0000-0002-2850-1419 | Data Science and Sharing Team, National Institute of Mental Health, Bethesda, MD, USA |\n"
  },
  {
    "path": ".maint/ROADMAP.md",
    "content": "# ROADMAP\n\nThis document states how the roadmap is built, discussed and monitored by the Maintainers, with the engagement of Contributors and PIs.\n\nFor example, if the GitHub Projects feature is used, this document will contain a link to the corresponding Project and document who is responsible of managing the project, contacting Contributors and Maintainers to monitor the progress of activities, organizing meetings and discussions around the activities, etc.\n\nThis document may also indicate how the *milestones* feature of the project is used and, as for Projects, who is responsible of what coordination actions, their frequence, etc.\n"
  },
  {
    "path": ".maint/requirements.txt",
    "content": "click\nfuzzywuzzy\npython-Levenshtein"
  },
  {
    "path": ".maint/update_authors.py",
    "content": "#!/usr/bin/env python3\n\"\"\"Update and sort the creators list of the zenodo record.\"\"\"\nimport json\nimport sys\nfrom pathlib import Path\n\nimport click\nfrom fuzzywuzzy import fuzz, process\n\n\ndef read_md_table(md_text):\n    \"\"\"\n    Extract the first table found in a markdown document as a Python dict.\n\n    Examples\n    --------\n    >>> read_md_table('''\n    ... # Some text\n    ...\n    ... More text\n    ...\n    ... | **Header1** | **Header2** |\n    ... | --- | --- |\n    ... | val1 | val2 |\n    ... |  | val4 |\n    ...\n    ... | **Header3** | **Header4** |\n    ... | --- | --- |\n    ... | val1 | val2 |\n    ... |  | val4 |\n    ... ''')\n    [{'header1': 'val1', 'header2': 'val2'}, {'header2': 'val4'}]\n\n    \"\"\"\n    prev = None\n    keys = None\n    retval = []\n    for line in md_text.splitlines():\n        if line.strip().startswith('| --- |'):\n            keys = (\n                k.replace('*', '').strip()\n                for k in prev.split('|')\n            )\n            keys = [k.lower() for k in keys if k]\n            continue\n        elif not keys:\n            prev = line\n            continue\n\n        if not line or not line.strip().startswith('|'):\n            break\n\n        values = [v.strip() or None for v in line.split('|')][1:-1]\n        retval.append({k: v for k, v in zip(keys, values) if v})\n\n    return retval\n\n\ndef sort_contributors(entries, git_lines, exclude=None, last=None):\n    \"\"\"Return a list of author dictionaries, ordered by contribution.\"\"\"\n    last = last or []\n    sorted_authors = sorted(entries, key=lambda i: i['name'])\n\n    first_last = [\n        ' '.join(val['name'].split(',')[::-1]).strip() for val in sorted_authors\n    ]\n    first_last_excl = [\n        ' '.join(val['name'].split(',')[::-1]).strip() for val in exclude or []\n    ]\n\n    unmatched = []\n    author_matches = []\n    for ele in git_lines:\n        matches = process.extract(\n            ele, first_last, scorer=fuzz.token_sort_ratio, limit=2\n        )\n        # matches is a list [('First match', % Match), ('Second match', % Match)]\n        if matches[0][1] > 80:\n            val = sorted_authors[first_last.index(matches[0][0])]\n        else:\n            # skip unmatched names\n            if ele not in first_last_excl:\n                unmatched.append(ele)\n            continue\n\n        if val not in author_matches:\n            author_matches.append(val)\n\n    names = {' '.join(val['name'].split(',')[::-1]).strip() for val in author_matches}\n    for missing_name in first_last:\n        if missing_name not in names:\n            missing = sorted_authors[first_last.index(missing_name)]\n            author_matches.append(missing)\n\n    position_matches = []\n    for i, item in enumerate(author_matches):\n        pos = item.pop('position', None)\n        if pos is not None:\n            position_matches.append((i, int(pos)))\n\n    for i, pos in position_matches:\n        if pos < 0:\n            pos += len(author_matches) + 1\n        author_matches.insert(pos, author_matches.pop(i))\n\n    return author_matches, unmatched\n\n\ndef get_git_lines(fname='line-contributors.txt'):\n    \"\"\"Run git-line-summary.\"\"\"\n    import shutil\n    import subprocess as sp\n\n    contrib_file = Path(fname)\n\n    lines = []\n    if contrib_file.exists():\n        print('WARNING: Reusing existing line-contributors.txt file.', file=sys.stderr)\n        lines = contrib_file.read_text().splitlines()\n\n    git_line_summary_path = shutil.which('git-line-summary')\n    if not git_line_summary_path:\n        git_line_summary_path = 'git summary --dedup-by-email'.split(' ')\n    else:\n        git_line_summary_path = [git_line_summary_path]\n\n    if not lines and git_line_summary_path:\n        print('Running git-line-summary on repo')\n        lines = sp.check_output(git_line_summary_path).decode().splitlines()\n        lines = [line for line in lines if 'Not Committed Yet' not in line]\n        contrib_file.write_text('\\n'.join(lines))\n\n    if not lines:\n        _msg = (\n            ': git-line-summary not found, please install git-extras '\n            * (git_line_summary_path is None)\n        )\n        raise RuntimeError(\n            f'Could not find line-contributors from git repository{_msg}.'\n        )\n    return [' '.join(line.strip().split()[1:-1]) for line in lines if '%' in line]\n\n\ndef _namelast(inlist):\n    retval = []\n    for i in inlist:\n        i['name'] = (f\"{i.pop('name', '')} {i.pop('lastname', '')}\").strip()\n        if not i['name']:\n            i['name'] = i.get('handle', '<Unknown Name>')\n        retval.append(i)\n    return retval\n\n\n@click.group()\ndef cli():\n    \"\"\"Generate authorship boilerplates.\"\"\"\n    pass\n\n\n@cli.command()\n@click.option('-z', '--zenodo-file', type=click.Path(exists=True), default='.zenodo.json')\n@click.option('-m', '--maintainers', type=click.Path(exists=True), default='.maint/MAINTAINERS.md')\n@click.option('-c', '--contributors', type=click.Path(exists=True),\n              default='.maint/CONTRIBUTORS.md')\n@click.option('--pi', type=click.Path(exists=True), default='.maint/PIs.md')\n@click.option('-f', '--former-file', type=click.Path(exists=True), default='.maint/FORMER.md')\ndef zenodo(\n    zenodo_file,\n    maintainers,\n    contributors,\n    pi,\n    former_file,\n):\n    \"\"\"Generate a new Zenodo payload file.\"\"\"\n    data = get_git_lines()\n\n    zenodo = json.loads(Path(zenodo_file).read_text())\n\n    former = _namelast(read_md_table(Path(former_file).read_text()))\n    zen_creators, miss_creators = sort_contributors(\n        _namelast(read_md_table(Path(maintainers).read_text())),\n        data,\n        exclude=former,\n    )\n\n    zen_contributors, miss_contributors = sort_contributors(\n        _namelast(read_md_table(Path(contributors).read_text())),\n        data,\n        exclude=former\n    )\n\n    zen_pi = _namelast(reversed(read_md_table(Path(pi).read_text())))\n\n    zenodo['creators'] = zen_creators\n    zenodo['contributors'] = zen_contributors + [\n        pi for pi in zen_pi if pi not in zen_contributors\n    ]\n    creator_names = {\n        c['name'] for c in zenodo['creators']\n        if c['name'] != '<Unknown Name>'\n    }\n\n    zenodo['contributors'] = [\n        c for c in zenodo['contributors']\n        if c['name'] not in creator_names\n    ]\n\n    misses = set(miss_creators).intersection(miss_contributors)\n    if misses:\n        print(\n            \"Some people made commits, but are missing in .maint/ \"\n            f\"files: {', '.join(misses)}\",\n            file=sys.stderr,\n        )\n\n    # Remove position\n    for creator in zenodo['creators']:\n        creator.pop('position', None)\n        creator.pop('handle', None)\n        if 'affiliation' not in creator:\n            creator['affiliation'] = 'Unknown affiliation'\n        elif isinstance(creator['affiliation'], list):\n            creator['affiliation'] = creator['affiliation'][0]\n\n    for creator in zenodo['contributors']:\n        creator.pop('handle', None)\n        creator['type'] = 'Researcher'\n        creator.pop('position', None)\n\n        if 'affiliation' not in creator:\n            creator['affiliation'] = 'Unknown affiliation'\n        elif isinstance(creator['affiliation'], list):\n            creator['affiliation'] = creator['affiliation'][0]\n\n    Path(zenodo_file).write_text(\n        f'{json.dumps(zenodo, indent=2)}\\n'\n    )\n\n\n@cli.command()\n@click.option('-m', '--maintainers', type=click.Path(exists=True), default='.maint/MAINTAINERS.md')\n@click.option('-c', '--contributors', type=click.Path(exists=True),\n              default='.maint/CONTRIBUTORS.md')\n@click.option('--pi', type=click.Path(exists=True), default='.maint/PIs.md')\n@click.option('-f', '--former-file', type=click.Path(exists=True), default='.maint/FORMER.md')\ndef publication(\n    maintainers,\n    contributors,\n    pi,\n    former_file,\n):\n    \"\"\"Generate the list of authors and affiliations for papers.\"\"\"\n    members = (\n        _namelast(read_md_table(Path(maintainers).read_text()))\n        + _namelast(read_md_table(Path(contributors).read_text()))\n    )\n    former_names = _namelast(read_md_table(Path(former_file).read_text()))\n\n    hits, misses = sort_contributors(\n        members,\n        get_git_lines(),\n        exclude=former_names,\n    )\n\n    pi_hits = _namelast(reversed(read_md_table(Path(pi).read_text())))\n    pi_names = [pi['name'] for pi in pi_hits]\n    hits = [\n        hit for hit in hits\n        if hit['name'] not in pi_names\n    ] + pi_hits\n\n    def _aslist(value):\n        if isinstance(value, (list, tuple)):\n            return value\n        return [value]\n\n    # Remove position\n    affiliations = []\n    for item in hits:\n        item.pop('position', None)\n        for a in _aslist(item.get('affiliation', 'Unaffiliated')):\n            if a not in affiliations:\n                affiliations.append(a)\n\n    aff_indexes = [\n        ', '.join(\n            [\n                '%d' % (affiliations.index(a) + 1)\n                for a in _aslist(author.get('affiliation', 'Unaffiliated'))\n            ]\n        )\n        for author in hits\n    ]\n\n    if misses:\n        print(\n            \"Some people made commits, but are missing in .maint/ \"\n            f\"files: {', '.join(misses)}\",\n            file=sys.stderr,\n        )\n\n    print('Authors (%d):' % len(hits))\n    print(\n        '{}.'.format('; '.join(\n            [\n                '{} \\\\ :sup:`{}`\\\\ '.format(i['name'], idx)\n                for i, idx in zip(hits, aff_indexes)\n            ]\n        ))\n    )\n\n    print(\n        '\\n\\nAffiliations:\\n{}'.format('\\n'.join(\n            [f'{i + 1: >2}. {a}' for i, a in enumerate(affiliations)]\n        ))\n    )\n\n\nif __name__ == '__main__':\n    \"\"\" Install entry-point \"\"\"\n    cli()\n"
  },
  {
    "path": ".maint/update_changes.sh",
    "content": "#!/bin/bash\n#\n# Collects the pull-requests since the latest release and\n# aranges them in the CHANGES.rst file.\n#\n# This is a script to be run before releasing a new version.\n#\n# Usage /bin/bash update_changes.sh 1.0.1\n#\n\n# Setting      # $ help set\nset -u         # Treat unset variables as an error when substituting.\nset -x         # Print command traces before executing command.\n\n# Check whether the Upcoming release header is present\nhead -1 CHANGES.rst | grep -q Upcoming\nUPCOMING=$?\nif [[ \"$UPCOMING\" == \"0\" ]]; then\n    head -n3  CHANGES.rst >> newchanges\nfi\n\n# Search for PRs since previous release\ngit show --pretty='format:  * %b %s'  HEAD | sed 's/Merge pull request \\#\\([^\\d]*\\)\\ from\\ .*/(\\#\\1)/' >> newchanges\n\n# Add back the Upcoming header if it was present\nif [[ \"$UPCOMING\" == \"0\" ]]; then\n    tail -n+4 CHANGES.rst >> newchanges\nelse\n    cat CHANGES.rst >> newchanges\nfi\n\n# Replace old CHANGES.rst with new file\nmv newchanges CHANGES.rst\n\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "# .readthedocs.yaml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details\n\n# Required\nversion: 2\n\n# Set the version of Python and other tools you might need\nbuild:\n  os: ubuntu-20.04\n  apt_packages:\n    - graphviz\n  tools:\n    python: \"3.9\"\n\n# Build documentation in the docs/ directory with Sphinx\nsphinx:\n   configuration: docs/source/conf.py\n\n# Install with pip install .[docs]\npython:\n  install:\n    - method: pip\n      path: .\n      extra_requirements:\n        - docs\n"
  },
  {
    "path": ".zenodo.json",
    "content": "{\n  \"title\": \"MRIQC: Advancing the automatic prediction of image quality in MRI from unseen sites\",\n  \"description\": \"<p>Automated Quality Control and visual reports for Quality Assessment of structural (T1w, T2w) and functional MRI of the brain <a href=http://mriqc.readthedocs.io>http://mriqc.readthedocs.io</a>.</p>\",\n  \"creators\": [\n    {\n      \"orcid\": \"0000-0001-8435-6191\",\n      \"affiliation\": \"Lausanne University Hospital and University of Lausanne, Lausanne, Switzerland\",\n      \"name\": \"Oscar Esteban\"\n    },\n    {\n      \"orcid\": \"0000-0002-6533-164X\",\n      \"affiliation\": \"Department of Psychology, Stanford University, CA, USA\",\n      \"name\": \"Christopher J. Markiewicz\"\n    },\n    {\n      \"orcid\": \"0000-0003-3715-7012\",\n      \"affiliation\": \"Department of Neuroimaging, Institute of Psychiatry, Psychology and Neuroscience, King's College London, London, UK\",\n      \"name\": \"Eilidh MacNicol\"\n    },\n    {\n      \"orcid\": \"0000-0002-1668-9629\",\n      \"affiliation\": \"Department of Radiology, Lausanne University Hospital and University of Lausanne, Switzerland\",\n      \"name\": \"C\\u00e9line Provins\"\n    },\n    {\n      \"orcid\": \"0000-0002-7454-8189\",\n      \"affiliation\": \"Psychology Department, University of Washington, Seattle, WA, USA\",\n      \"name\": \"McKenzie P. Hagen\"\n    }\n  ],\n  \"contributors\": [\n    {\n      \"orcid\": \"0000-0001-7159-1387\",\n      \"affiliation\": \"Quantivly Inc., Somerville, MA, USA\",\n      \"name\": \"Zvi Baratz\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"affiliation\": \"The University of Washington eScience Institute, WA, USA\",\n      \"name\": \"Teresa Gomez\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0003-4613-6643\",\n      \"affiliation\": \"Section on Clinical and Computational Psychiatry, National Institute of Mental Health, Bethesda, MD, USA\",\n      \"name\": \"Dylan Nielson\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"affiliation\": \"Functional MRI Facility, National Institute of Mental Health, Bethesda, MD, USA\",\n      \"name\": \"Jan Varada\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0003-3007-1056\",\n      \"affiliation\": \"Department of Psychology, Stanford University, CA, USA\",\n      \"name\": \"Ross W. Blair\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0003-0679-1985\",\n      \"affiliation\": \"The University of Washington eScience Institute, WA, USA\",\n      \"name\": \"Ariel Rokem\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-1242-8990\",\n      \"affiliation\": \"NeuroSpin, CEA, Universit\\u00e9 Paris-Saclay, NeuroSpin, Gif-sur-Yvette, France\",\n      \"name\": \"Dimitri Papadopoulos Orfanos\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-9546-1306\",\n      \"affiliation\": \"University of Florida: Gainesville, Florida, US\",\n      \"name\": \"William Triplett\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-7252-7771\",\n      \"affiliation\": \"Department of Psychology, Stanford University, CA, USA\",\n      \"name\": \"Mathias Goncalves\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"affiliation\": \"CRC ULiege, Liege, Belgium\",\n      \"name\": \"Nikita Beliy\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0001-5884-4247\",\n      \"affiliation\": \"Quansight, Dublin, Ireland\",\n      \"name\": \"John A. Lee\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-9661-1396\",\n      \"affiliation\": \"Brigham and Women's Hospital, Mass General Brigham, Harvard Medical School, MA, USA\",\n      \"name\": \"Jon Haitz Legarreta Gorro\\u00f1o\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0003-4141-1343\",\n      \"affiliation\": \"Johns Hopkins Bloomberg School of Public Health, MD, USA\",\n      \"name\": \"Patrick Sadil\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0001-6377-3885\",\n      \"affiliation\": \"Department of Neuroscience, University of Pennsylvania, PA, USA\",\n      \"name\": \"Ursula A. Tooley\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0003-3456-2493\",\n      \"affiliation\": \"Psychological and Brain Sciences Department, Dartmouth College, NH, USA\",\n      \"name\": \"Yaroslav O. Halchenko\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0003-2882-0900\",\n      \"affiliation\": \"McGovern Institute for Brain Research, Massachusetts Institute of Technology, Cambridge, USA\",\n      \"name\": \"Yibei Chen\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-4892-2659\",\n      \"affiliation\": \"Department of Psychology, University of Texas at Austin, TX, USA\",\n      \"name\": \"James D. Kent\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"affiliation\": \"University of Michigan, Ann Arbor, USA\",\n      \"name\": \"Bennet Fauber\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0001-9813-3167\",\n      \"affiliation\": \"Department of Psychology, Florida International University, FL, USA\",\n      \"name\": \"Taylor Salo\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"affiliation\": \"Max Planck Institute for Human Development, Berlin, Germany\",\n      \"name\": \"Michael Krause\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-5749-6049\",\n      \"affiliation\": \"Center for Brain Imaging, New York University, NY, USA\",\n      \"name\": \"Pablo Velasco\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-4516-5103\",\n      \"affiliation\": \"Oxford Big Data Institute, University of Oxford, Oxford, GB\",\n      \"name\": \"Thomas Nichols\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"affiliation\": \"Department of Physics, Imperial College London, London, UK\",\n      \"name\": \"Adam Huffman\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-3896-6906\",\n      \"affiliation\": \"Department of Radiology, Lausanne University Hospital and University of Lausanne, Switzerland\",\n      \"name\": \"Elodie Savary\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-0657-6516\",\n      \"affiliation\": \"Charit\\u00e9 Berlin, Berlin, Germany\",\n      \"name\": \"Johannes Achtzehn\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0001-9030-2202\",\n      \"affiliation\": \"Department of Psychology, Stanford University, CA, USA\",\n      \"name\": \"Joke Durnez\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-5312-6729\",\n      \"affiliation\": \"McGovern Institute for Brain Research, MIT, MA, USA; and Department of Otolaryngology, Harvard Medical School, MA, USA\",\n      \"name\": \"Satrajit S. Ghosh\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"affiliation\": \"Center for Innovation in Brain Science, University of Arizona, Tucson, AZ, USA\",\n      \"name\": \"Adam C. Raikes\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-9402-2184\",\n      \"affiliation\": \"Computational Neuroimaging Lab, BioCruces Health Research Institute\",\n      \"name\": \"Asier Erramuzpe\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"affiliation\": \"Washington University School of Medicine, St.Louis, MO, USA\",\n      \"name\": \"Benjamin Kay\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0003-3748-6289\",\n      \"affiliation\": \"Department of Psychology, Stanford University, CA, USA\",\n      \"name\": \"Daniel Birman\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-2666-0969\",\n      \"affiliation\": \"International Committee of the Red Cross - ICRC, Geneva, Switzerland\",\n      \"name\": \"Michael Dayan\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"affiliation\": \"National Institutes of Health, USA\",\n      \"name\": \"Michael G. Clark\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0001-9332-1580\",\n      \"affiliation\": \"Institute of Psychiatry, Psychology & Neuroscience, King's College London, London, UK\",\n      \"name\": \"Rafael Garcia-Dias\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0002-2850-1419\",\n      \"affiliation\": \"Data Science and Sharing Team, National Institute of Mental Health, Bethesda, MD, USA\",\n      \"name\": \"Adam G. Thomas\",\n      \"type\": \"Researcher\"\n    },\n    {\n      \"orcid\": \"0000-0001-6755-0259\",\n      \"affiliation\": \"Department of Psychology, Stanford University, CA, USA\",\n      \"name\": \"Russell A. Poldrack\",\n      \"type\": \"Researcher\"\n    }\n  ],\n  \"keywords\": [\n    \"neuroimaging\",\n    \"workflow\",\n    \"pipeline\",\n    \"quality-control\",\n    \"fMRI\",\n    \"BIDS\"\n  ],\n  \"related_identifiers\": [\n    {\n      \"identifier\": \"http://mriqc.readthedocs.io\",\n      \"relation\": \"documents\",\n      \"scheme\": \"url\"\n    },\n    {\n      \"identifier\": \"10.1371/journal.pone.0184661\",\n      \"relation\": \"isPartOf\",\n      \"scheme\": \"doi\"\n    }\n  ],\n  \"license\": \"Apache-2.0\",\n  \"upload_type\": \"software\"\n}\n"
  },
  {
    "path": "AGENTS.md",
    "content": "# Agent Instructions for Codex\n\n## Operating Principles\n1. **Always plan first.** Provide a brief plan before any changes and do the hardest thinking during the planning phase.\n2. **Highlight critical points.** When proposing tasks, call out steps that could introduce side effects (e.g., migrations, API changes, backward-compatibility concerns).\n3. **Ask one question at a time.** If a decision is needed from a user, ask a single, clear question before proceeding.\n\n## Branch Naming\n- **Format:** `<type>/<short-description>`\n- **Examples:**\n  - `fix/one-off-bug-gh134`\n  - `enh/add-bids-validator`\n  - `doc/update-readme`\n- If working from a tracked issue, include the issue number in the branch name (e.g., `fix/segfault-gh134`).\n\n## Commit Messages (Conventional Commits)\n- **Must strictly follow** Conventional Commits: https://www.conventionalcommits.org/en/v1.0.0/\n- Examples:\n  - `fix(parser): handle empty token stream`\n  - `docs(readme): add quickstart example`\n  - `chore(ci): pin action versions`\n\n## Pull Request Titles & Descriptions\n- **PR Title format:** `TYPE: Short summary`\n  - Type is **all caps**, from a conventional-commit-like set (e.g., FIX, ENH, MNT, DOC, CI, CHORE).\n  - **No scope parenthetical** in PR titles.\n- **Examples:**\n  - `FIX: Address one-off bug in traversing list`\n  - `DOC: Complete docstring with missing arguments`\n- **PR Description should include:**\n  - Summary of changes\n  - Motivation / context\n  - Testing performed (exact commands)\n\n## Linting & Pre-Change Validation\n- **Always run linting before proposing changes.**\n- This repository uses the GitHub Actions linting command:\n  - `pipx run ruff format --diff`\n- If linting cannot run due to environment constraints, state the limitation and suggest a retry in a fully configured environment.\n"
  },
  {
    "path": "CHANGES.rst",
    "content": "25.0.0 (TBD)\n============\nA new 25.x series.\n\nCHANGES\n-------\n  * FIX: Decode bytes to produce a string (#1371)\n  * FIX: Containers report wrong version (#1357)\n  * FIX: Elapsed run time format (#1366)\n  * FIX: Passing ``bytes`` into ``str.join()`` (#1356)\n  * FIX: Optimize interface to minimize memory fingerprint (#1351)\n  * ENH: Remove redundant *DIPY* function (#1369)\n  * DOC: Add a HPC troubleshooting section to the documentation (#1349)\n  * DOC: Provide a range of years ending with current for the copyright statement (#1359)\n  * DOC: Fix missing code block start (#1368)\n  * DOC: Reorganize documentation and redirect to *NiPreps* docs (#1367)\n\n24.0.2 (August 26, 2024)\n========================\nA patch release with bugfixes and enhancements.\n\nCHANGES\n-------\n\n* FIX: Pin latest *NiReports* release (24.0.2) addressing ``fMRIPlot`` issues by @oesteban (`#1342 <https://github.com/nipreps/mriqc/pull/1342>`__)\n* FIX: Edge artifacts in first and last slices due to interpolation by @oesteban (`#1338 <https://github.com/nipreps/mriqc/pull/1338>`__)\n* FIX: Normalize bids-filters' modality keys to be lowercase by @oesteban (`#1332 <https://github.com/nipreps/mriqc/pull/1332>`__)\n* ENH: Add license NOTICE to start banner by @oesteban (`#1343 <https://github.com/nipreps/mriqc/pull/1343>`__)\n* ENH: Enable writing crashfiles in compressed-pickle format by @oesteban (`#1339 <https://github.com/nipreps/mriqc/pull/1339>`__)\n* ENH: Use ``orjson`` to serialize JSON, addressing *Numpy* serialization issues by @oesteban (`#1337 <https://github.com/nipreps/mriqc/pull/1337>`__)\n* ENH: Handle WebAPI timeouts more gently by @oesteban (`#1336 <https://github.com/nipreps/mriqc/pull/1336>`__)\n\n\n24.0.1 (August 20, 2024)\n========================\nA patch release with a large number of bugfixes (mostly focusing on memory issues), maintenance\nactivities, and metadata crawling before *Nipype* kicks in as a major optimization.\n\nWith thanks to @jhlegarreta for his first contribution in `#1293 <https://github.com/nipreps/mriqc/pull/1293>__`.\n\n.. admonition:: Author list for papers based on *MRIQC* 24.0 series\n\n    As described in the `Contributor Guidelines\n    <https://www.nipreps.org/community/CONTRIBUTING/#recognizing-contributions>`__,\n    anyone listed as a developer or contributor may write and submit manuscripts\n    about *MRIQC*.\n    To do so, please move the author(s) name(s) to the front of the following list:\n\n    Christopher J. Markiewicz \\ :sup:`1`\\ ; Zvi Baratz \\ :sup:`2`\\ ; Eilidh MacNicol \\ :sup:`3`\\ ; Céline Provins \\ :sup:`4`\\ ; Teresa Gomez \\ :sup:`5`\\ ; Dylan Nielson \\ :sup:`6`\\ ; Ross W. Blair \\ :sup:`1`\\ ; Jan Varada \\ :sup:`7`\\ ; Dimitri Papadopoulos Orfanos \\ :sup:`8`\\ ; William Triplett \\ :sup:`9`\\ ; Mathias Goncalves \\ :sup:`1`\\ ; Nikita Beliy \\ :sup:`10`\\ ; John A. Lee \\ :sup:`11`\\ ; Yibei Chen \\ :sup:`12`\\ ; Ursula A. Tooley \\ :sup:`13`\\ ; Patrick Sadil \\ :sup:`14`\\ ; Yaroslav O. Halchenko \\ :sup:`15`\\ ; James D. Kent \\ :sup:`16`\\ ; Taylor Salo \\ :sup:`17`\\ ; Bennet Fauber \\ :sup:`18`\\ ; Thomas Nichols \\ :sup:`19`\\ ; Pablo Velasco \\ :sup:`20`\\ ; Michael Krause \\ :sup:`21`\\ ; Jon Haitz Legarreta Gorroño \\ :sup:`22`\\ ; Satrajit S. Ghosh \\ :sup:`23`\\ ; Joke Durnez \\ :sup:`1`\\ ; Johannes Achtzehn \\ :sup:`24`\\ ; Elodie Savary \\ :sup:`4`\\ ; Adam Huffman \\ :sup:`25`\\ ; Rafael Garcia-Dias \\ :sup:`26`\\ ; Michael G. Clark \\ :sup:`27`\\ ; Michael Dayan \\ :sup:`28`\\ ; McKenzie P. Hagen \\ :sup:`29`\\ ; Daniel Birman \\ :sup:`1`\\ ; Benjamin Kay \\ :sup:`30`\\ ; Asier Erramuzpe \\ :sup:`31`\\ ; Adam C. Raikes \\ :sup:`32`\\ ; Adam G. Thomas \\ :sup:`33`\\ ; Russell A. Poldrack \\ :sup:`1`\\ ; Ariel Rokem \\ :sup:`5`\\ ; Oscar Esteban \\ :sup:`4`\\ .\n\n    Affiliations:\n\n      1. Department of Psychology, Stanford University, CA, USA\n      2. Quantivly Inc., Somerville, MA, USA\n      3. Department of Neuroimaging, Institute of Psychiatry, Psychology and Neuroscience, King's College London, London, UK\n      4. Department of Radiology, Lausanne University Hospital and University of Lausanne, Switzerland\n      5. The University of Washington eScience Institute, WA, USA\n      6. Section on Clinical and Computational Psychiatry, National Institute of Mental Health, Bethesda, MD, USA\n      7. Functional MRI Facility, National Institute of Mental Health, Bethesda, MD, USA\n      8. NeuroSpin, CEA, Université Paris-Saclay, NeuroSpin, Gif-sur-Yvette, France\n      9. University of Florida: Gainesville, Florida, US\n      10. CRC ULiege, Liege, Belgium\n      11. Quansight, Dublin, Ireland\n      12. McGovern Institute for Brain Research, Massachusetts Institute of Technology, Cambridge, USA\n      13. Department of Neuroscience, University of Pennsylvania, PA, USA\n      14. Johns Hopkins Bloomberg School of Public Health, MD, USA\n      15. Psychological and Brain Sciences Department, Dartmouth College, NH, USA\n      16. Department of Psychology, University of Texas at Austin, TX, USA\n      17. Department of Psychology, Florida International University, FL, USA\n      18. University of Michigan, Ann Arbor, USA\n      19. Oxford Big Data Institute, University of Oxford, Oxford, GB\n      20. Center for Brain Imaging, New York University, NY, USA\n      21. Max Planck Institute for Human Development, Berlin, Germany\n      22. Brigham and Women's Hospital, Mass General Brigham, Harvard Medical School, MA, USA\n      23. McGovern Institute for Brain Research, MIT, MA, USA; and Department of Otolaryngology, Harvard Medical School, MA, USA\n      24. Charité Berlin, Berlin, Germany\n      25. Department of Physics, Imperial College London, London, UK\n      26. Institute of Psychiatry, Psychology & Neuroscience, King's College London, London, UK\n      27. National Institutes of Health, USA\n      28. International Committee of the Red Cross - ICRC, Geneva, Switzerland\n      29. Psychology Department, University of Washington, Seattle, WA, USA\n      30. Washington University School of Medicine, St.Louis, MO, USA\n      31. Computational Neuroimaging Lab, BioCruces Health Research Institute\n      32. Center for Innovation in Brain Science, University of Arizona, Tucson, AZ, USA\n      33. Data Science and Sharing Team, National Institute of Mental Health, Bethesda, MD, USA\n\nCHANGES\n-------\n\n* FIX: Multiecho fMRI crashing with 'unhashable type' errors by @oesteban in https://github.com/nipreps/mriqc/pull/1295\n* FIX: Set ``n_procs`` instead of ``num_threads`` on node ``apply_hmc`` by @oesteban in https://github.com/nipreps/mriqc/pull/1309\n* FIX: Address memory issues by limiting ``BigPlot``'s parallelization. by @oesteban in https://github.com/nipreps/mriqc/pull/1320\n* FIX: Address memory issues in the DWI pipeline by @oesteban in https://github.com/nipreps/mriqc/pull/1323\n* FIX: Limit IQMs' node number of processes and, therefore, memory by @oesteban in https://github.com/nipreps/mriqc/pull/1325\n* FIX: Resolve numeric overflow in drift estimation node by @oesteban in https://github.com/nipreps/mriqc/pull/1324\n* FIX: Revise bugfix #1324 by @oesteban in https://github.com/nipreps/mriqc/pull/1327\n* FIX: Remove unreachable code within DWI pipeline by @oesteban in https://github.com/nipreps/mriqc/pull/1328\n* ENH: Allow moving the cache folder with an environment variable by @oesteban in https://github.com/nipreps/mriqc/pull/1285\n* ENH: Flatten multi-echo lists in circumstances that they fail by @oesteban in https://github.com/nipreps/mriqc/pull/1286\n* ENH: Added type hints to config module by @zvi-quantivly in https://github.com/nipreps/mriqc/pull/1288\n* ENH: Add test for the CLI parser by @jhlegarreta in https://github.com/nipreps/mriqc/pull/1293\n* ENH: Add CLI entry point test by @jhlegarreta in https://github.com/nipreps/mriqc/pull/1294\n* ENH: Add a development Dockerfile for testing local changes to the repo. by @rwblair in https://github.com/nipreps/mriqc/pull/1299\n* ENH: Crawl dataset's metadata only once and before Nipype's workflow by @oesteban in https://github.com/nipreps/mriqc/pull/1317\n* ENH(dMRI): Deal gracefully with small CC masks by @oesteban in https://github.com/nipreps/mriqc/pull/1311\n* ENH: Leverage new spun-off apply interface by @oesteban in https://github.com/nipreps/mriqc/pull/1313\n* MAINT: Removed personal information from maintainers and updated in contributors by @zvi-quantivly in https://github.com/nipreps/mriqc/pull/1289\n* MAINT: Add JHLegarreta to the contributors list by @jhlegarreta in https://github.com/nipreps/mriqc/pull/1301\n* MAINT: Flexibilize pandas pinned version by @oesteban in https://github.com/nipreps/mriqc/pull/1310\n* MAINT: Remove *Pandas*'s ``FutureWarning`` by @oesteban in https://github.com/nipreps/mriqc/pull/1326\n* DOC: Add description of ``summary_fg`` to the documentation by @celprov in https://github.com/nipreps/mriqc/pull/1306\n* STY: Apply ruff/flake8-implicit-str-concat rule ISC001 by @DimitriPapadopoulos in https://github.com/nipreps/mriqc/pull/1296\n* STY: Format *Jupyter notebooks* by @oesteban in https://github.com/nipreps/mriqc/pull/1321\n\n**Full Changelog**: https://github.com/nipreps/mriqc/compare/24.0.0...24.0.1\n\n24.0.0 (April 17, 2024)\n=======================\nInitial major release of 2024, featuring the **extraction of IQMs from DWI data**\nfor the first time in *MRIQC*'s timeline.\n\nCHANGES\n-------\n\n* FIX: Bug in *toml* loader crashing with mixed arrays in config (#1281)\n* FIX: Remove *DataLad* as a node (#1278)\n* FIX: Calculation of trivial shells (#1276)\n* FIX: Finalized naming and connection of DWI IQMs (#1272)\n* FIX: Enable group reports for DWI (#1266)\n* FIX: Address issues that had broken the group reports (#1262)\n* FIX: Select filters if modalities are selected (#1261)\n* FIX: Make sure new logs and config file output are compatible with parallel processes (#1259)\n* FIX: Skip short BOLD runs that break outlier detection (#1120)\n* FIX: Revise config save/load and update inputs after dropping (#1245)\n* FIX: Drift should not be estimated when less than three low-b volumes present (#1242, #1243)\n* FIX: Handle ``NUMEXPR_MAX_THREADS`` like ``OMP_NUM_THREADS`` (#1241)\n* FIX: Exclude DWI runs with insufficient orientations or missing bvals (#1240)\n* FIX: Avert costly ``BIDSLayout.__repr__`` calls when saving config (#1239)\n* FIX: Duplicate node in anatomical workflow (#1234)\n* FIX: Typo in ``sorted(..., reverse=True)`` call (#1211)\n* ENH: Fail for non-formatted code (#1274)\n* ENH: Annotate nodes with ``n_procs`` to allow safe parallelization (#1277)\n* ENH: Mechanism to protect config's fields and write out config (#1258)\n* ENH: Improve documentation and logging of *SynthStrip*'s model (#1254)\n* ENH: Improve logging of runtime (#1253)\n* ENH: Expose a command-line option for minimum DWI volumes (#1249)\n* ENH: Improve error handling and logging (#1238)\n* ENH: Add *b*-vector angular deviations as IQMs (#1233)\n* ENH: Move from DTI to DKI with multishell data (#1230)\n* ENH: Noise floor estimated with PCA (``dwidenoise``) as an IQM (#1229)\n* ENH: Integrate PIESNO noise mask and sigma estimation (#1227)\n* ENH: Use MAD for robust estimation of sigma in the CC mask (#1228)\n* ENH: Add new IQM for DWI → NDC (#1226)\n* ENH: Add FA-based IQMs (nans percentage and degenerate percentage) (#1225)\n* ENH: Add computation of spiking voxels mask and percent IQMs (#1224)\n* ENH: Adds diffusion-related IQMs. (#1131)\n* ENH: Revise summary stats extraction and include controlled roundings (#1219)\n* DOC: Add changelog to documentation (#1217)\n* MAINT: Added ruff to development dependencies (#1271)\n* MAINT: Removed pre-commit from development dependencies (#1269)\n* MAINT: Clean up more ``FutureWarning`` issued by *Pandas* (#1257)\n* MAINT: Prevent pandas-originating deprecation warning (#1251)\n* MAINT: Move GitHub Actions and config files from *flake8* → *ruff* (#1212)\n* MAINT: Update contributor affiliation in ``CONTRIBUTORS.md`` (#1214)\n* STY: Reformat diffusion workflows module (#1279)\n* STY: Applied ruff formatting (#1273)\n\n23.1.1 (March 20, 2024)\n=======================\nA long-overdue hotfix release addressing many bugs generated during the development\nof the new dMRI workflows, and some relating to improvements of the handling of\nmulti-echo fMRI.\nThe release also include one year-worth of maintenance actions and a general code\ncleanup with *Ruff*.\n\nCHANGES\n-------\n\n* FIX: Missing connection to head-motion estimation node in DWI workflow by `@oesteban <https://github.com/@oesteban>`__ in `#1207 <https://github.com/nipreps/mriqc/pull/1207>`__\n* FIX: Revise porting to ``Loader`` by `@oesteban <https://github.com/@oesteban>`__ in `#1201 <https://github.com/nipreps/mriqc/pull/1201>`__\n* FIX: Revise the last two sloppy merges by `@oesteban <https://github.com/@oesteban>`__ in `#1200 <https://github.com/nipreps/mriqc/pull/1200>`__\n* FIX: Move from ``pkg_resources`` to ``niworkflows.data.Loader`` by `@oesteban <https://github.com/@oesteban>`__ in `#1199 <https://github.com/nipreps/mriqc/pull/1199>`__\n* FIX: DIPY not listed as a dependency by `@oesteban <https://github.com/@oesteban>`__ in `#1197 <https://github.com/nipreps/mriqc/pull/1197>`__\n* FIX: Include ``dwidenoise`` within Docker image by `@oesteban <https://github.com/@oesteban>`__ in `#1196 <https://github.com/nipreps/mriqc/pull/1196>`__\n* FIX: Copy name attribute of ``dataset_description.json`` from input dataset by `@celprov <https://github.com/@celprov>`__ in `#1187 <https://github.com/nipreps/mriqc/pull/1187>`__\n* FIX: Remove FD as an ``iterfield`` in ``MapNode`` causing crash with ME-BOLD by `@celprov <https://github.com/@celprov>`__ in `#1179 <https://github.com/nipreps/mriqc/pull/1179>`__\n* FIX: Incorrect plugin metadata passed to *Report Assembler* by `@oesteban <https://github.com/@oesteban>`__ in `#1188 <https://github.com/nipreps/mriqc/pull/1188>`__\n* FIX: Temporary fix of the missing ``\"dwi\"`` key by `@celprov <https://github.com/@celprov>`__ in `#1174 <https://github.com/nipreps/mriqc/pull/1174>`__\n* FIX: Rearrange multi-echo report by `@celprov <https://github.com/@celprov>`__ in `#1164 <https://github.com/nipreps/mriqc/pull/1164>`__\n* FIX: Typo in ``inputnode`` field in dMRI masking workflow by `@celprov <https://github.com/@celprov>`__ in `#1165 <https://github.com/nipreps/mriqc/pull/1165>`__\n* FIX: Bug in group level workflow by `@celprov <https://github.com/@celprov>`__ in `#1148 <https://github.com/nipreps/mriqc/pull/1148>`__\n* FIX: Bugs in DWI workflow by `@celprov <https://github.com/@celprov>`__ in `#1147 <https://github.com/nipreps/mriqc/pull/1147>`__\n* FIX: Use simpler DWI reference workflow by `@yibeichan <https://github.com/@yibeichan>`__ in `#1145 <https://github.com/nipreps/mriqc/pull/1145>`__\n* FIX: Drop deprecated *Networkx*'s API by `@celprov <https://github.com/@celprov>`__ in `#1137 <https://github.com/nipreps/mriqc/pull/1137>`__\n* FIX: Replace ``np.float`` by ``np.float64`` by `@celprov <https://github.com/@celprov>`__ in `#1140 <https://github.com/nipreps/mriqc/pull/1140>`__\n* ENH: Improved logging and optimize early checkpoint on subjects by `@oesteban <https://github.com/@oesteban>`__ in `#1198 <https://github.com/nipreps/mriqc/pull/1198>`__\n* ENH: Store confound timeseries data by `@psadil <https://github.com/@psadil>`__ in `#1166 <https://github.com/nipreps/mriqc/pull/1166>`__\n* ENH: Large overhaul of the functional workflow w/focus on ME-EPI by `@oesteban <https://github.com/@oesteban>`__ in `#1155 <https://github.com/nipreps/mriqc/pull/1155>`__\n* ENH: Implement BIDS filters file and drop legacy BIDS querying by `@oesteban <https://github.com/@oesteban>`__ in `#1154 <https://github.com/nipreps/mriqc/pull/1154>`__\n* ENH: Swap background and zoomed-in visualizations in anatomical reports by `@oesteban <https://github.com/@oesteban>`__ in `#1151 <https://github.com/nipreps/mriqc/pull/1151>`__\n* MAINT: Test on *Python* 3.12 by `@DimitriPapadopoulos <https://github.com/@DimitriPapadopoulos>`__ in `#1156 <https://github.com/nipreps/mriqc/pull/1156>`__\n* MAINT: Disable flaky T1w test on CircleCI by `@oesteban <https://github.com/@oesteban>`__ in `#1202 <https://github.com/nipreps/mriqc/pull/1202>`__\n* MAINT: Overhaul of the ``Dockerfile`` by `@oesteban <https://github.com/@oesteban>`__ in `#1195 <https://github.com/nipreps/mriqc/pull/1195>`__\n* MAINT: Revise package's extra dependencies by `@oesteban <https://github.com/@oesteban>`__ in `#1194 <https://github.com/nipreps/mriqc/pull/1194>`__\n* MAINT: Clean up some ``setuptools_scm`` remnants by `@oesteban <https://github.com/@oesteban>`__ in `#1193 <https://github.com/nipreps/mriqc/pull/1193>`__\n* MAINT: Load ``FMRISummary`` from *NiReports* rather than *NiWorkflows* by `@celprov <https://github.com/@celprov>`__ in `#1167 <https://github.com/nipreps/mriqc/pull/1167>`__\n* MAINT: Update to latest *migas*' API by `@mgxd <https://github.com/@mgxd>`__ in `#1160 <https://github.com/nipreps/mriqc/pull/1160>`__\n* MAINT: Update bold to large resource class in ``config.yml`` by `@oesteban <https://github.com/@oesteban>`__ in `#1158 <https://github.com/nipreps/mriqc/pull/1158>`__\n* MAINT: Refresh cached intermediate results by `@oesteban <https://github.com/@oesteban>`__ in `#1143 <https://github.com/nipreps/mriqc/pull/1143>`__\n* MAINT: Simplify GitHub actions checks and update action versions by `@effigies <https://github.com/@effigies>`__ in `#1141 <https://github.com/nipreps/mriqc/pull/1141>`__\n* MAINT: Python 3.11 is supported by `@DimitriPapadopoulos <https://github.com/@DimitriPapadopoulos>`__ in `#1123 <https://github.com/nipreps/mriqc/pull/1123>`__\n* MAINT: Apply suggestions from pyupgrade by `@DimitriPapadopoulos <https://github.com/@DimitriPapadopoulos>`__ in `#1124 <https://github.com/nipreps/mriqc/pull/1124>`__\n* DOC: Update *Sphinx* pinned version to 5 by `@oesteban <https://github.com/@oesteban>`__ in `#1192 <https://github.com/nipreps/mriqc/pull/1192>`__\n* DOC: http:// → https:// by `@DimitriPapadopoulos <https://github.com/@DimitriPapadopoulos>`__ in `#1126 <https://github.com/nipreps/mriqc/pull/1126>`__\n* DOC: Add info on the *FreeSurfer* requirement for bare install to address #1034 by `@neurorepro <https://github.com/@neurorepro>`__ in `#1130 <https://github.com/nipreps/mriqc/pull/1130>`__\n* STY: Add *Ruff* config and fix all warnings and errors by `@oesteban <https://github.com/@oesteban>`__ in `#1203 <https://github.com/nipreps/mriqc/pull/1203>`__\n* STY: Remove extraneous parentheses by `@DimitriPapadopoulos <https://github.com/@DimitriPapadopoulos>`__ in `#1186 <https://github.com/nipreps/mriqc/pull/1186>`__\n* STY: Apply a few refurb suggestions by `@DimitriPapadopoulos <https://github.com/@DimitriPapadopoulos>`__ in `#1162 <https://github.com/nipreps/mriqc/pull/1162>`__\n* STY: Fix typo found by codespell by `@DimitriPapadopoulos <https://github.com/@DimitriPapadopoulos>`__ in `#1161 <https://github.com/nipreps/mriqc/pull/1161>`__\n\n23.1.0 (June 14, 2023)\n======================\nA new minor release featuring the new individual reports built with the new\n*NiReports* VRS (visual reports system). This means *MRIQC* now uses the same\npackage *fMRIPrep* uses for generating its reports. In addition to that,\nthis new release also features *Beta* support for diffusion MRI (dMRI). \n\nCHANGES\n-------\n\n* FIX: Better handling of BIDS cached indexation (`#1121 <https://github.com/nipreps/mriqc/pull/1121>`__)\n* FIX: Make doctest of ``NumberOfShells`` more reliable (`#1122 <https://github.com/nipreps/mriqc/pull/1122>`__)\n* FIX: Add protection for NaNs and INFs when calculating QI2 (`#1112 <https://github.com/nipreps/mriqc/pull/1112>`__)\n* FIX: ``PlotMosaic`` expects lists, not tuples (`#1111 <https://github.com/nipreps/mriqc/pull/1111>`__)\n* FIX: BIDS database directory handling (`#1110 <https://github.com/nipreps/mriqc/pull/1110>`__)\n* FIX: Remove unused dipy import in the functional interfaces (`#1109 <https://github.com/nipreps/mriqc/pull/1109>`__)\n* FIX: Refine the head mask after removal of FSL BET (`#1107 <https://github.com/nipreps/mriqc/pull/1107>`__)\n* FIX: Inform *SynthStrip* about the desired intraop threads (`#1101 <https://github.com/nipreps/mriqc/pull/1101>`__)\n* FIX: Test broken by #1098 (`#1100 <https://github.com/nipreps/mriqc/pull/1100>`__)\n* FIX: Separate report bootstrap files (anat vs. func) (`#1098 <https://github.com/nipreps/mriqc/pull/1098>`__)\n* FIX: Propagate logging level to subprocesses (`#1030 <https://github.com/nipreps/mriqc/pull/1030>`__)\n* ENH: Incorporate new NiReports' DWI heatmaps (`#1119 <https://github.com/nipreps/mriqc/pull/1119>`__)\n* ENH: More compact of shell-wise summary statistic maps (avg/std) (`#1116 <https://github.com/nipreps/mriqc/pull/1116>`__)\n* ENH: Add a basic DTI fitting into the diffusion workflow (`#1115 <https://github.com/nipreps/mriqc/pull/1115>`__)\n* ENH: MRIQC for DWI (`#1113 <https://github.com/nipreps/mriqc/pull/1113>`__)\n* ENH: Culminate dropping FSL as a dependency (`#1108 <https://github.com/nipreps/mriqc/pull/1108>`__)\n* ENH: Replace FSL FAST with ANTs Atropos for brain tissue segmentation (`#1099 <https://github.com/nipreps/mriqc/pull/1099>`__)\n* ENH: Drop FSL MELODIC (without alternative) (`#1106 <https://github.com/nipreps/mriqc/pull/1106>`__)\n* ENH: Drop FSL BET to estimate the \"outskin\" (head) mask (`#1105 <https://github.com/nipreps/mriqc/pull/1105>`__)\n* ENH: Drop utilization of \"head\" mask from template (`#1104 <https://github.com/nipreps/mriqc/pull/1104>`__)\n* ENH: Move templates' probsegs into individual at normalization (`#1103 <https://github.com/nipreps/mriqc/pull/1103>`__)\n* ENH: Improving the resource monitor -- infer PID from process name (`#1049 <https://github.com/nipreps/mriqc/pull/1049>`__) (`#1049 <https://github.com/nipreps/mriqc/pull/1049>`__)\n* ENH: Refactor reports system to use *NiReports* and the general VRS (`#1085 <https://github.com/nipreps/mriqc/pull/1085>`__)\n* MAINT: Move codespell configuration to ``pyproject.toml`` (`#1097 <https://github.com/nipreps/mriqc/pull/1097>`__)\n* MAINT: Update deprecated ``nibabel.spatialimage.get_data()`` calls (`#1096 <https://github.com/nipreps/mriqc/pull/1096>`__)\n\n.. admonition:: Author list for papers based on *MRIQC* 23.0 series\n\n    As described in the `Contributor Guidelines\n    <https://www.nipreps.org/community/CONTRIBUTING/#recognizing-contributions>`__,\n    anyone listed as developer or contributor may write and submit manuscripts\n    about *MRIQC*.\n    To do so, please move the author(s) name(s) to the front of the following list:\n    \n    Zvi Baratz \\ :sup:`1`\\ ; Christopher J. Markiewicz \\ :sup:`2`\\ ; Eilidh MacNicol \\ :sup:`3`\\ ; Dylan Nielson \\ :sup:`4`\\ ; Jan Varada \\ :sup:`5`\\ ; Ross W. Blair \\ :sup:`2`\\ ; Céline Provins \\ :sup:`6`\\ ; William Triplett \\ :sup:`7`\\ ; Mathias Goncalves \\ :sup:`2`\\ ; Nikita Beliy \\ :sup:`8`\\ ; John A. Lee \\ :sup:`9`\\ ; Ursula A. Tooley \\ :sup:`10`\\ ; James D. Kent \\ :sup:`11`\\ ; Yaroslav O. Halchenko \\ :sup:`12`\\ ; Bennet Fauber \\ :sup:`13`\\ ; Taylor Salo \\ :sup:`14`\\ ; Michael Krause \\ :sup:`15`\\ ; Pablo Velasco \\ :sup:`16`\\ ; Thomas Nichols \\ :sup:`17`\\ ; Adam Huffman \\ :sup:`18`\\ ; Elodie Savary \\ :sup:`6`\\ ; Johannes Achtzehn \\ :sup:`19`\\ ; Joke Durnez \\ :sup:`2`\\ ; Satrajit S. Ghosh \\ :sup:`20`\\ ; Asier Erramuzpe \\ :sup:`21`\\ ; Benjamin Kay \\ :sup:`22`\\ ; Daniel Birman \\ :sup:`2`\\ ; McKenzie P. Hagen \\ :sup:`23`\\ ; Michael G. Clark \\ :sup:`24`\\ ; Patrick Sadil \\ :sup:`25`\\ ; Rafael Garcia-Dias \\ :sup:`26`\\ ; Adam G. Thomas \\ :sup:`27`\\ ; Russell A. Poldrack \\ :sup:`2`\\ ; Ariel Rokem \\ :sup:`28`\\ ; Oscar Esteban \\ :sup:`6`\\ .\n\n    Affiliations:\n\n      1. Sagol School of Neuroscience, Tel Aviv University, Tel Aviv, Israel\n      2. Department of Psychology, Stanford University, CA, USA\n      3. Department of Neuroimaging, Institute of Psychiatry, Psychology and Neuroscience, King's College London, London, UK\n      4. Section on Clinical and Computational Psychiatry, National Institute of Mental Health, Bethesda, MD, USA\n      5. Functional MRI Facility, National Institute of Mental Health, Bethesda, MD, USA\n      6. Department of Radiology, Lausanne University Hospital and University of Lausanne, Switzerland\n      7. University of Florida: Gainesville, Florida, US\n      8. CRC ULiege, Liege, Belgium\n      9. Quansight, Dublin, Ireland\n      10. Department of Neuroscience, University of Pennsylvania, PA, USA\n      11. Department of Psychology, University of Texas at Austin, TX, USA\n      12. Psychological and Brain Sciences Department, Dartmouth College, NH, USA\n      13. University of Michigan, Ann Arbor, USA\n      14. Department of Psychology, Florida International University, FL, USA\n      15. Max Planck Institute for Human Development, Berlin, Germany\n      16. Center for Brain Imaging, New York University, NY, USA\n      17. Oxford Big Data Institute, University of Oxford, Oxford, GB\n      18. Department of Physics, Imperial College London, London, UK\n      19. Charité Berlin, Berlin, Germany\n      20. McGovern Institute for Brain Research, MIT, MA, USA; and Department of Otolaryngology, Harvard Medical School, MA, USA\n      21. Computational Neuroimaging Lab, BioCruces Health Research Institute\n      22. Washington University School of Medicine, St.Louis, MO, USA\n      23. Psychology Department, University of Washington, Seattle, WA, USA\n      24. National Institutes of Health, USA\n      25. Johns Hopkins Bloomberg School of Public Health, MD, USA\n      26. Institute of Psychiatry, Psychology & Neuroscience, King's College London, London, UK\n      27. Data Science and Sharing Team, National Institute of Mental Health, Bethesda, MD, USA\n      28. The University of Washington eScience Institute, WA, USA\n\n23.0.1 (March 24, 2023)\n=======================\nA hotfix release resolving a reggression introduced with the new optimized indexing.\n\n* FIX: Underspecified regex sets ``BIDSLayout`` to ignore data with sessions (`#1094 <https://github.com/nipreps/mriqc/pull/1094>`__)\n* FIX: Input data has incompatible dimensionality (plotting ICA) (`#1082 <https://github.com/nipreps/mriqc/pull/1082>`__)\n* ENH: Optimize metadata gathering reusing ``BIDSLayout`` db (`#1084 <https://github.com/nipreps/mriqc/pull/1084>`__)\n* DOC : update anatomical example report in documentation (`#1088 <https://github.com/nipreps/mriqc/pull/1088>`__)\n* MAINT: Drop old ``mriqc_plot`` script (`#1091 <https://github.com/nipreps/mriqc/pull/1091>`__)\n\n23.0.0 (March 10, 2023)\n=======================\nThe new 23.0.x series include several prominent changes.\nVisualization has been migrated from *MRIQC* and *niworkflows* over to the new *NiReports* project.\nThis series include a major bugfix with **the optimization of the indexing** of the input BIDS folder,\nwhich was taking large times with sizeable datasets.\nTelemetry has also been incorporated with *migas*.\nThese new series also involve maintenance housekeeping, and includes some relevant bugfixes.\n\nNew contributors\n----------------\n\n* `@arokem <https://github.com/arokem>`__ made their first contribution in `#1040 <https://github.com/nipreps/mriqc/pull/1040>`__\n* `@yarikoptic <https://github.com/yarikoptic>`__ made their first contribution in `#1057 <https://github.com/nipreps/mriqc/pull/1057>`__\n* `@esavary <https://github.com/esavary>`__ made their first contribution in `#1047 <https://github.com/nipreps/mriqc/pull/1047>`__\n\nCHANGES\n-------\n**Full Changelog**: https://github.com/nipreps/mriqc/compare/22.0.6...23.0.0\n\n* FIX: Send metadata extraction to workers (functional workflow) (`#1081 <https://github.com/nipreps/mriqc/pull/1081>`__)\n* FIX: Plot coronal as main plain for mosaic of rodent images (`#1027 <https://github.com/nipreps/mriqc/pull/1027>`__)\n* FIX: Address non-empty take from empty axes (anatomical IQMs) (`#1077 <https://github.com/nipreps/mriqc/pull/1077>`__)\n* FIX: Uniformize building workflow message (anat vs. func) (`#1072 <https://github.com/nipreps/mriqc/pull/1072>`__)\n* FIX: Move telemetry atexit into entrypoint func (`#1067 <https://github.com/nipreps/mriqc/pull/1067>`__)\n* FIX: Preempt PyBIDS to spend time indexing non-BIDS folders (`#1050 <https://github.com/nipreps/mriqc/pull/1050>`__)\n* FIX: Update T1w metrics (`#1063 <https://github.com/nipreps/mriqc/pull/1063>`__)\n* FIX: Resource monitor would not ever start tracking (`#1051 <https://github.com/nipreps/mriqc/pull/1051>`__)\n* ENH: Add DataLad getter to inputs of functional workflows (`#1071 <https://github.com/nipreps/mriqc/pull/1071>`__)\n* ENH: Add migas telemetry (`#1036 <https://github.com/nipreps/mriqc/pull/1036>`__)\n* ENH: Add codespell automation: config, action, and typos fixed (`#1057 <https://github.com/nipreps/mriqc/pull/1057>`__)\n* MAINT: Update *NiReports* calls to upcoming interfaces API (`#1078 <https://github.com/nipreps/mriqc/pull/1078>`__)\n* MAINT: Pacify codespell (`#1080 <https://github.com/nipreps/mriqc/pull/1080>`__)\n* MAINT: Conclude porting of reportlets into *NiReports* (`#1068 <https://github.com/nipreps/mriqc/pull/1068>`__)\n* MAINT: Migrate to hatchling (`#1070 <https://github.com/nipreps/mriqc/pull/1070>`__)\n* MAINT: Pin PyBIDS 0.15.6 (culminating #1050) (`#1069 <https://github.com/nipreps/mriqc/pull/1069>`__)\n* MAINT: Update niworkflows pin to support newer ANTs releases (`#1047 <https://github.com/nipreps/mriqc/pull/1047>`__)\n* MAINT: Fix minor aspects of WebAPI deployment on CircleCI (`#1064 <https://github.com/nipreps/mriqc/pull/1064>`__)\n* MAINT: Update CircleCI executor and use built-in docker-compose (`#1061 <https://github.com/nipreps/mriqc/pull/1061>`__)\n* MAINT: Rotate CircleCI secrets and setup up org-level context (`#1046 <https://github.com/nipreps/mriqc/pull/1046>`__)\n* DOC: Update documentation with the new carpet plot (`#1045 <https://github.com/nipreps/mriqc/pull/1045>`__)\n* DOC: Complete the documentation of ``summary_stats()`` (`#1044 <https://github.com/nipreps/mriqc/pull/1044>`__)\n* DOC: Fixes a couple of broken links to the *nipype* documentation (`#1040 <https://github.com/nipreps/mriqc/pull/1040>`__)\n\n.. admonition:: Author list for papers based on *MRIQC* 23.0 series\n\n    As described in the `Contributor Guidelines\n    <https://www.nipreps.org/community/CONTRIBUTING/#recognizing-contributions>`__,\n    anyone listed as developer or contributor may write and submit manuscripts\n    about *MRIQC*.\n    To do so, please move the author(s) name(s) to the front of the following list:\n\n    Zvi Baratz \\ :sup:`1`\\ ; Christopher J. Markiewicz \\ :sup:`2`\\ ; Eilidh MacNicol \\ :sup:`3`\\ ; Dylan Nielson \\ :sup:`4`\\ ; Jan Varada \\ :sup:`5`\\ ; Ross W. Blair \\ :sup:`2`\\ ; Céline Provins \\ :sup:`6`\\ ; William Triplett \\ :sup:`7`\\ ; Mathias Goncalves \\ :sup:`2`\\ ; Nikita Beliy \\ :sup:`8`\\ ; John A. Lee \\ :sup:`9`\\ ; Ursula A. Tooley \\ :sup:`10`\\ ; James D. Kent \\ :sup:`11`\\ ; Yaroslav O. Halchenko \\ :sup:`12`\\ ; Bennet Fauber \\ :sup:`13`\\ ; Taylor Salo \\ :sup:`14`\\ ; Michael Krause \\ :sup:`15`\\ ; Pablo Velasco \\ :sup:`16`\\ ; Thomas Nichols \\ :sup:`17`\\ ; Adam Huffman \\ :sup:`18`\\ ; Johannes Achtzehn \\ :sup:`19`\\ ; Joke Durnez \\ :sup:`2`\\ ; Satrajit S. Ghosh \\ :sup:`20`\\ ; Asier Erramuzpe \\ :sup:`21`\\ ; Benjamin Kay \\ :sup:`22`\\ ; Daniel Birman \\ :sup:`2`\\ ; Elodie Savary \\ :sup:`23`\\ ; McKenzie P. Hagen \\ :sup:`24`\\ ; Michael G. Clark \\ :sup:`25`\\ ; Patrick Sadil \\ :sup:`26`\\ ; Rafael Garcia-Dias \\ :sup:`27`\\ ; Adam G. Thomas \\ :sup:`28`\\ ; Russell A. Poldrack \\ :sup:`2`\\ ; Ariel Rokem \\ :sup:`29`\\ ; Oscar Esteban \\ :sup:`30`\\ .\n\n    Affiliations:\n\n      1. Sagol School of Neuroscience, Tel Aviv University, Tel Aviv, Israel\n      2. Department of Psychology, Stanford University, CA, USA\n      3. Department of Neuroimaging, Institute of Psychiatry, Psychology and Neuroscience, King's College London, London, UK\n      4. Section on Clinical and Computational Psychiatry, National Institute of Mental Health, Bethesda, MD, USA\n      5. Functional MRI Facility, National Institute of Mental Health, Bethesda, MD, USA\n      6. Lausanne University Hospital and University of Lausanne, Lausanne, Switzerland\n      7. University of Florida: Gainesville, Florida, US\n      8. CRC ULiege, Liege, Belgium\n      9. Quansight, Dublin, Ireland\n      10. Department of Neuroscience, University of Pennsylvania, PA, USA\n      11. Department of Psychology, University of Texas at Austin, TX, USA\n      12. Psychological and Brain Sciences Department, Dartmouth College, NH, USA\n      13. University of Michigan, Ann Arbor, USA\n      14. Department of Psychology, Florida International University, FL, USA\n      15. Max Planck Institute for Human Development, Berlin, Germany\n      16. Center for Brain Imaging, New York University, NY, USA\n      17. Oxford Big Data Institute, University of Oxford, Oxford, GB\n      18. Department of Physics, Imperial College London, London, UK\n      19. Charité Berlin, Berlin, Germany\n      20. McGovern Institute for Brain Research, MIT, MA, USA; and Department of Otolaryngology, Harvard Medical School, MA, USA\n      21. Computational Neuroimaging Lab, BioCruces Health Research Institute\n      22. Washington University School of Medicine, St.Louis, MO, USA\n      23. Department of Radiology, Lausanne University Hospital and University of Lausanne, Switzerland\n      24. Psychology Department, University of Washington, Seattle, WA, USA\n      25. National Institutes of Health, USA\n      26. Johns Hopkins Bloomberg School of Public Health, MD, USA\n      27. Institute of Psychiatry, Psychology & Neuroscience, King's College London, London, UK\n      28. Data Science and Sharing Team, National Institute of Mental Health, Bethesda, MD, USA\n      29. The University of Washington eScience Institute, WA, USA\n      30. Department of Radiology, Lausanne University Hospital and University of Lausanne\n\n22.0.6 (August 24, 2022)\n========================\nA hotfix release partially rolling-back the previous fix #1025.\nThanks everyone for your patience with the excessively rushed release of 22.0.5.\n\n* FIX: Better fix to the multi-argument ``--participant-label`` issue (`#1026 <https://github.com/nipreps/mriqc/pull/1026>`__)\n\n22.0.5 (August 24, 2022)\n========================\nA hotfix release addressing a problem with the argument parser.\n\n* FIX: Multiple valued ``--participant-label`` wrongly parsed (`#1025 <https://github.com/nipreps/mriqc/pull/1025>`__)\n\n22.0.4 (August 23, 2022)\n========================\nA hotfix release to ensure smooth operation of datalad within Docker.\n\n* FIX: Major improvements to new datalad-based interface & perform within containers (`#1024 <https://github.com/nipreps/mriqc/pull/1024>`__)\n* ENH: Bump Docker base to latest release (`#1022 <https://github.com/nipreps/mriqc/pull/1022>`__)\n\n22.0.3 (August 19, 2022)\n========================\nA patch release containing a bugfix to the SynthStrip preprocessing.\n\n* FIX: SynthStrip preprocessing miscalculating new shape after reorientation (`#1021 <https://github.com/nipreps/mriqc/pull/1021>`__)\n* ENH: Remove slice-timing correction (`#1019 <https://github.com/nipreps/mriqc/pull/1019>`__)\n* ENH: Add a new ``DataladIdentityInterface`` (`#1020 <https://github.com/nipreps/mriqc/pull/1020>`__)\n* ENH: Set rat-specific defaults for FD calculations (`#1005 <https://github.com/nipreps/mriqc/pull/1005>`__)\n* ENH: New version of the rating widget (`#1012 <https://github.com/nipreps/mriqc/pull/1012>`__)\n* DOC: Move readthedocs to use the config v2 file (YAML) (`#1018 <https://github.com/nipreps/mriqc/pull/1018>`__)\n* MAINT: Fix statsmodels dependency, it is not optional (`#1017 <https://github.com/nipreps/mriqc/pull/1017>`__)\n* MAINT: Several critical updates to CircleCI and Docker images (`#1016 <https://github.com/nipreps/mriqc/pull/1016>`__)\n* MAINT: Update the T1w IQMs to the new reference after #997 (`#1014 <https://github.com/nipreps/mriqc/pull/1014>`__)\n* MAINT: Fix failing tests as ``python setup.py`` is deprecated (`#1013 <https://github.com/nipreps/mriqc/pull/1013>`__)\n\n22.0.2 (August 15, 2022)\n========================\nA patch release including the new ratings widget.\n\n* ENH: New version of the rating widget (`#1012 <https://github.com/nipreps/mriqc/pull/1012>`__)\n* DOC: Move readthedocs to use the config v2 file (YAML) (`#1018 <https://github.com/nipreps/mriqc/pull/1018>`__)\n* MAINT: Fix ``statsmodels`` dependency, it is not optional (`#1017 <https://github.com/nipreps/mriqc/pull/1017>`__)\n* MAINT: Several critical updates to CircleCI and Docker images (`#1016 <https://github.com/nipreps/mriqc/pull/1016>`__)\n* MAINT: Update the T1w IQMs to the new reference after #997 (`#1014 <https://github.com/nipreps/mriqc/pull/1014>`__)\n* MAINT: Fix failing tests as ``python setup.py`` is deprecated (`#1013 <https://github.com/nipreps/mriqc/pull/1013>`__)\n\n22.0.1 (May 3rd, 2022)\n======================\nA patch release addressing a new minor bug.\n\n* FIX: More lenient handling of skull-stripped datasets (`#997 <https://github.com/nipreps/mriqc/pull/997>`__)\n\n22.0.0 (May 3rd, 2022)\n======================\nFirst official release after migrating the repository into the *NiPreps*' organization.\nA major new feature is the rodent pipeline by Eilidh MacNicol (@eilidhmacnicol).\nA second major feature is the adoption of the updated carpet plots for BOLD fMRI,\ncontributed by Céline Provins (@celprov).\nVirtual memory allocation has been ten-fold cut down, and a complementary resource monitor instrumentation is now available with *MRIQC*.\nThis release updates the Docker image with up-to-date dependencies, updates\n*MRIQC*'s codebase to the latest *NiTransforms* and includes some minor bugfixes.\nThe code, modules and data related to the MRIQC classifier have been extracted into an\nisolated package called [*MRIQC-learn*](https://github.com/nipreps/mriqc-learn).\nFinally, this release also contains a major code style overhaul by Zvi Baratz.\n\nThe contributor/author crediting system has been adapted to the current draft of the\n*NiPreps Community* Governance documents.\n\nWith thanks to @ZviBaratz, @nbeliy, @octomike, @benkay86, @verdurin, @leej3, @utooley,\nand @jAchtzehn for their contributions.\n\n* FIX: Inconsistent API in anatomical CNR computation (`#995 <https://github.com/nipreps/mriqc/pull/995>`__)\n* FIX: Check sanity of input data before extracting IQMs (`#994 <https://github.com/nipreps/mriqc/pull/994>`__)\n* FIX: Plot segmentations after dropping off-diagonal (`#989 <https://github.com/nipreps/mriqc/pull/989>`__)\n* FIX: Replace all deprecated ``nibabel.get_data()`` in anatomical module (`#988 <https://github.com/nipreps/mriqc/pull/988>`__)\n* FIX: Resource profiler was broken with config file (`#981 <https://github.com/nipreps/mriqc/pull/981>`__)\n* FIX: preserve WM segments in rodents (`#979 <https://github.com/nipreps/mriqc/pull/979>`__)\n* FIX: Pin ``jinja2 < 3.1`` (`#978 <https://github.com/nipreps/mriqc/pull/978>`__)\n* FIX: Make toml config unique, works around #912 (`#960 <https://github.com/nipreps/mriqc/pull/960>`__)\n* FIX: Nipype multiproc plugin expects ``n_procs`` and not ``nprocs`` (`#961 <https://github.com/nipreps/mriqc/pull/961>`__)\n* FIX: Set TR when generating carpetplots (enables time for X axis) (`#971 <https://github.com/nipreps/mriqc/pull/971>`__)\n* FIX: ``template_resolution`` deprecation warning (`#941 <https://github.com/nipreps/mriqc/pull/941>`__)\n* FIX: Set entity ``datatype`` in ``BIDSLayout`` queries (`#942 <https://github.com/nipreps/mriqc/pull/942>`__)\n* FIX: T2w image of MNI template unavailable in Singularity (`#940 <https://github.com/nipreps/mriqc/pull/940>`__)\n* FIX: Release process -- Docker deployment not working + Python package lacks WebAPI token (`#938 <https://github.com/nipreps/mriqc/pull/938>`__)\n* FIX: Revise building documentation at RTD after migration (`#935 <https://github.com/nipreps/mriqc/pull/935>`__)\n* FIX: Final touch-ups in the maintenance of Docker image + CI (`#928 <https://github.com/nipreps/mriqc/pull/928>`__)\n* FIX: Update unit tests (`#927 <https://github.com/nipreps/mriqc/pull/927>`__)\n* FIX: Update dependencies and repair BOLD workflow accordingly (`#926 <https://github.com/nipreps/mriqc/pull/926>`__)\n* FIX: Update dependencies and repair T1w workflow accordingly (`#925 <https://github.com/nipreps/mriqc/pull/925>`__)\n* FIX: Set ``matplotlib`` on ``Agg`` output mode (`#892 <https://github.com/nipreps/mriqc/pull/892>`__)\n* ENH: Deprecate ``--start-idx`` / ``--stop-idx`` (`#993 <https://github.com/nipreps/mriqc/pull/993>`__)\n* ENH: Add SynthStrip base module (`#987 <https://github.com/nipreps/mriqc/pull/987>`__)\n* ENH: Improve building workflow message feedback (`#990 <https://github.com/nipreps/mriqc/pull/990>`__)\n* ENH: Add instrumentation to monitor resources (`#984 <https://github.com/nipreps/mriqc/pull/984>`__)\n* ENH: Standalone, lightweight version of MultiProc plugin (`#985 <https://github.com/nipreps/mriqc/pull/985>`__)\n* ENH: Revise plugin and workflow initialization (`#983 <https://github.com/nipreps/mriqc/pull/983>`__)\n* ENH: Base generalization of the pipeline for rodents (`#969 <https://github.com/nipreps/mriqc/pull/969>`__)\n* ENH: Update to new *NiWorkflows*' API, which adds the crown to the carpetplot (`#968 <https://github.com/nipreps/mriqc/pull/968>`__)\n* ENH: Optimize *PyBIDS*' layout initialization (`#939 <https://github.com/nipreps/mriqc/pull/939>`__)\n* ENH: Refactored long strings to a :mod:`mriqc.messages` module (`#901 <https://github.com/nipreps/mriqc/pull/901>`__)\n* ENH: Refactored :mod:`mriqc.interfaces.common` module (`#901 <https://github.com/nipreps/mriqc/pull/901>`__)\n* DOC: Improve documentation of ``--nprocs`` and ``--omp-nthreads`` (`#986 <https://github.com/nipreps/mriqc/pull/986>`__)\n* DOC: Add ``sbatch`` file example for SLURM execution (`#963 <https://github.com/nipreps/mriqc/pull/963>`__)\n* DOC: Various fixes to \"Running mriqc\" section (`#897 <https://github.com/nipreps/mriqc/pull/897>`__)\n* MAINT: Refactor ``Dockerfile`` using new miniconda image (`#974 <https://github.com/nipreps/mriqc/pull/974>`__)\n* MAINT: Outsource the classifier into nipreps/mriqc-learn (`#973 <https://github.com/nipreps/mriqc/pull/973>`__)\n* MAINT: Update ``CONTRIBUTORS.md`` (`#953 <https://github.com/nipreps/mriqc/pull/953>`__)\n* MAINT: Update contributor location (`#952 <https://github.com/nipreps/mriqc/pull/952>`__)\n* MAINT: Updates to ``CONTRIBUTORS.md`` file\n* MAINT: Revise Docker image settings & CircleCI (`#937 <https://github.com/nipreps/mriqc/pull/937>`__)\n* MAINT: Finalize transfer to ``nipreps`` organization (`#936 <https://github.com/nipreps/mriqc/pull/936>`__)\n* MAINT: Relicensing to Apache-2.0, for compliance with *NiPreps* and prior transfer to the org (`#930 <https://github.com/nipreps/mriqc/pull/930>`__)\n* MAINT: New Docker layer caching system of other *NiPreps* (`#929 <https://github.com/nipreps/mriqc/pull/929>`__)\n* MAINT: Code style overhaul (`#901 <https://github.com/nipreps/mriqc/pull/901>`__)\n* MAINT: Update ``Dockerfile`` and catch-up with *fMRIPrep*'s (`#924 <https://github.com/nipreps/mriqc/pull/924>`__)\n* STY: Run ``black`` at the top of the repo (`#932 <https://github.com/nipreps/mriqc/pull/932>`__)\n\n**Full Changelog**: https://github.com/nipreps/mriqc/compare/0.16.1...22.0.0\n\n.. admonition:: Author list for papers based on *MRIQC* 22.0.x\n\n    As described in the `Contributor Guidelines\n    <https://www.nipreps.org/community/CONTRIBUTING/#recognizing-contributions>`__,\n    anyone listed as developer or contributor may write and submit manuscripts\n    about *MRIQC*.\n    To do so, please move the author(s) name(s) to the front of the following list:\n\n    Zvi Baratz \\ :sup:`1`\\ ; Christopher J. Markiewicz \\ :sup:`2`\\ ; Eilidh MacNicol \\ :sup:`3`\\ ; Dylan Nielson \\ :sup:`4`\\ ; Jan Varada \\ :sup:`5`\\ ; Ross W. Blair \\ :sup:`2`\\ ; William Triplett \\ :sup:`6`\\ ; Nikita Beliy \\ :sup:`7`\\ ; Céline Provins \\ :sup:`8`\\ ; John A. Lee \\ :sup:`9`\\ ; Ursula A. Tooley \\ :sup:`10`\\ ; James D. Kent \\ :sup:`11`\\ ; Bennet Fauber \\ :sup:`12`\\ ; Taylor Salo \\ :sup:`13`\\ ; Mathias Goncalves \\ :sup:`2`\\ ; Michael Krause \\ :sup:`14`\\ ; Pablo Velasco \\ :sup:`15`\\ ; Thomas Nichols \\ :sup:`16`\\ ; Adam Huffman \\ :sup:`17`\\ ; Johannes Achtzehn \\ :sup:`18`\\ ; Joke Durnez \\ :sup:`2`\\ ; Satrajit S. Ghosh \\ :sup:`19`\\ ; Asier Erramuzpe \\ :sup:`20`\\ ; Benjamin Kay \\ :sup:`21`\\ ; Daniel Birman \\ :sup:`2`\\ ; Michael G. Clark \\ :sup:`22`\\ ; Rafael Garcia-Dias \\ :sup:`23`\\ ; Sean Marret \\ :sup:`5`\\ ; Adam G. Thomas \\ :sup:`24`\\ ; Russell A. Poldrack \\ :sup:`2`\\ ; Krzysztof J. Gorgolewski \\ :sup:`25`\\ ; Oscar Esteban \\ :sup:`26`\\ .\n\n    Affiliations:\n\n    1. Sagol School of Neuroscience, Tel-Aviv University\n    2. Department of Psychology, Stanford University, CA, USA\n    3. Department of Neuroimaging, Institute of Psychiatry, Psychology and Neuroscience, King's College London, London, UK\n    4. Section on Clinical and Computational Psychiatry, National Institute of Mental Health, Bethesda, MD, USA\n    5. Functional MRI Facility, National Institute of Mental Health, Bethesda, MD, USA\n    6. University of Florida: Gainesville, Florida, US\n    7. CRC ULiege, Liege, Belgium\n    8. Lausanne University Hospital and University of Lausanne, Lausanne, Switzerland\n    9. Quansight, Dublin, Ireland\n    10. Department of Neuroscience, University of Pennsylvania, PA, USA\n    11. Department of Psychology, University of Texas at Austin, TX, USA\n    12. University of Michigan, Ann Arbor, USA\n    13. Department of Psychology, Florida International University, FL, USA\n    14. Max Planck Institute for Human Development, Berlin, Germany\n    15. Center for Brain Imaging, New York University, NY, USA\n    16. Oxford Big Data Institute, University of Oxford, Oxford, GB\n    17. Department of Physics, Imperial College London, London, UK\n    18. Charité Berlin, Berlin, Germany\n    19. McGovern Institute for Brain Research, MIT, MA, USA; and Department of Otolaryngology, Harvard Medical School, MA, USA\n    20. Computational Neuroimaging Lab, BioCruces Health Research Institute\n    21. Washington University School of Medicine, St.Louis, MO, USA\n    22. National Institutes of Health, USA\n    23. Institute of Psychiatry, Psychology & Neuroscience, King's College London, London, UK\n    24. Data Science and Sharing Team, National Institute of Mental Health, Bethesda, MD, USA\n    25. Google LLC\n    26. Department of Radiology, Lausanne University Hospital and University of Lausanne\n\nSeries 0.16.x\n=============\n0.16.1 (January 30, 2021)\n-------------------------\nBug-fix release in 0.16.x series.\n\nThis PR improves BIDS Derivatives compliance, fixes an issue with reading datasets with\nsubjects of the form ``sub-sXYZ``, and improves compatibility with more recent matplotlib.\n\n* FIX: Participant labels starting with ``[sub]`` cannot be used (`#890 <https://github.com/nipreps/mriqc/pull/890>`__)\n* FIX: Change deprecated ``normed`` to ``density`` in parameters to ``hist()`` (`#888 <https://github.com/nipreps/mriqc/pull/888>`__)\n* ENH: Write derivatives metadata (`#885 <https://github.com/nipreps/mriqc/pull/885>`__)\n* ENH: Add ``--pdb`` option to make debugging easier (`#884 <https://github.com/nipreps/mriqc/pull/884>`__)\n\n0.16.0 (January 5, 2021)\n------------------------\nNew feature release in 0.16.x series.\n\nThis version removes the FSL dependency from the fMRI workflow.\n\n* FIX: Skip version cache on read-only filesystems (`#862 <https://github.com/nipreps/mriqc/pull/862>`__)\n* FIX: Honor ``$OMP_NUM_THREADS`` environment variable (`#848 <https://github.com/nipreps/mriqc/pull/848>`__)\n* RF: Simplify comprehensions, using easy-to-read var names (`#875 <https://github.com/nipreps/mriqc/pull/875>`__)\n* RF: Free the fMRI workflow from FSL (`#842 <https://github.com/nipreps/mriqc/pull/842>`__)\n* CI: Fix up Circle builds (`#876 <https://github.com/nipreps/mriqc/pull/876>`__)\n* CI: Update machine images on Circle (`#874 <https://github.com/nipreps/mriqc/pull/874>`__)\n\nOlder (unsupported) series\n==========================\n0.15.3 (September 18, 2020)\n---------------------------\nA bugfix release to re-enable setting of ``--omp-nthreads/--ants-nthreads``.\n\n* FIX: ``omp_nthreads`` typo (`#846 <https://github.com/nipreps/mriqc/pull/846>`__)\n\n0.15.2 (April 6, 2020)\n----------------------\nA bugfix release containing mostly maintenance actions and documentation\nimprovements. This version drops Python 3.5.\nThe core of MRIQC has adopted the config-module pattern from fMRIPrep.\nWith thanks to A. Erramuzpe, @justbennet, U. Tooley, and A. Huffman\nfor contributions.\n\n* MAINT: revise style of all files (except for workflows) (`#839 <https://github.com/nipreps/mriqc/pull/839>`__)\n* MAINT: Clear the clutter of warnings (`#838 <https://github.com/nipreps/mriqc/pull/838>`__)\n* RF: Adopt config module pattern from *fMRIPrep* (`#837 <https://github.com/nipreps/mriqc/pull/837>`__)\n* MAINT: Clear the clutter of warnings (`#838 <https://github.com/nipreps/mriqc/pull/838>`__)\n* MAINT: Drop Python 3.5, simplify linting (`#833 <https://github.com/nipreps/mriqc/pull/833>`__)\n* MAINT: Update to latest Ubuntu Xenial tag (`#814 <https://github.com/nipreps/mriqc/pull/814>`__)\n* MAINT: Centralize all requirements and versions on ``setup.cfg`` (`#819 <https://github.com/nipreps/mriqc/pull/819>`__)\n* MAINT: Use recent Python image to build packages in CircleCI (`#808 <https://github.com/nipreps/mriqc/pull/808>`__)\n* DOC: Improve AQI (and other IQMs) and boxplot whiskers descriptions (`#816 <https://github.com/nipreps/mriqc/pull/816>`__)\n* DOC: Refactor how documentation is built on CircleCI (`#818 <https://github.com/nipreps/mriqc/pull/818>`__)\n* DOC: Corrected a couple of typos in ``--help`` text (`#809 <https://github.com/nipreps/mriqc/pull/809>`__)\n\n0.15.1 (July 26, 2019)\n----------------------\nA maintenance patch release updating PyBIDS.\n\n* FIX: ``FileNotFoundError`` when MELODIC (``--ica``) does not converge (`#800 <https://github.com/nipreps/mriqc/pull/800>`__) @oesteban\n* MAINT: Migrate MRIQC to a ``setup.cfg`` style of installation (`#799 <https://github.com/nipreps/mriqc/pull/799>`__) @oesteban\n* MAINT: Use PyBIDS 0.9.2+ via niworkflows PR (`#796 <https://github.com/nipreps/mriqc/pull/796>`__) @effigies\n\n0.15.0 (April 5, 2019)\n----------------------\nA long overdue update, pinning updated versions of\n`TemplateFlow <https://doi.org/10.5281/zenodo.2583289>`__ and\n`Niworkflows <https://github.com/nipreps/niworkflows>`__.\nWith thanks to @garciadias for contributions.\n\n* ENH: Revision of QI2 (`#606 <https://github.com/nipreps/mriqc/pull/606>`__) @oesteban\n* FIX: Set matplotlib backend early (`#759 <https://github.com/nipreps/mriqc/pull/759>`__) @oesteban\n* FIX: Niworkflows pin <0.5 (`#766 <https://github.com/nipreps/mriqc/pull/766>`__) @oesteban\n* DOC: Update BIDS validation link. (`#764 <https://github.com/nipreps/mriqc/pull/764>`__) @garciadias\n* DOC: Add data sharing agreement (`#765 <https://github.com/nipreps/mriqc/pull/765>`__) @oesteban\n* FIX: Catch uncaught exception in WebAPI upload. (`#774 <https://github.com/nipreps/mriqc/pull/774>`__) @rwblair\n* FIX/DOC: Append new line after dashes in ``mriqc_run`` help text (`#777 <https://github.com/nipreps/mriqc/pull/777>`__) @rwblair\n* ENH: Use TemplateFlow and niworkflows-0.8.x (`#782 <https://github.com/nipreps/mriqc/pull/782>`__) @oesteban\n* FIX: Correctly set WebAPI rating endpoint in BOLD reports. (`#785 <https://github.com/nipreps/mriqc/pull/785>`__) @oesteban\n* FIX: Correctly process values of rating widget (`#787 <https://github.com/nipreps/mriqc/pull/787>`__) @oesteban\n\n0.14.2 (August 20, 2018)\n------------------------\n\n* FIX: Preempt pandas resolving ``Path`` objects (`#746 <https://github.com/nipreps/mriqc/pull/746>`__) @oesteban\n* FIX: Codacy issues (`#745 <https://github.com/nipreps/mriqc/pull/745>`__) @oesteban\n\n0.14.1 (August 20, 2018)\n------------------------\n\n* FIX: Calculate relative path with sessions (`#742 <https://github.com/nipreps/mriqc/pull/742>`__) @oesteban\n* ENH: Add a toggle button to rating widget (`#743 <https://github.com/nipreps/mriqc/pull/743>`__) @oesteban\n\n0.14.0 (August 17, 2018)\n------------------------\n\n* ENH: New feedback widget (`#740 <https://github.com/nipreps/mriqc/pull/740>`__) @oesteban\n\n0.13.1 (August 16, 2018)\n------------------------\n\n* [ENH,FIX] Updates to individual reports, fix table after rating (`#739 <https://github.com/nipreps/mriqc/pull/739>`__) @oesteban\n\n0.13.0 (August 15, 2018)\n------------------------\n\n* MAINT: Overdue refactor (`#736 <https://github.com/nipreps/mriqc/pull/736>`__) @oesteban\n  * FIX: Reorganize outputs (closes #396)\n  * ENH: Memory usage - lessons learned with FMRIPREP (`#703 <https://github.com/nipreps/mriqc/pull/703>`__)\n  * FIX: Cannot allocate memory (v 0.9.4) (closes #536)\n  * FIX: Drop inoperative ``--report-dir`` flag (`#550 <https://github.com/nipreps/mriqc/pull/550>`__)\n  * FIX: Drop misleading WARNING of the group-level execution (`#714 <https://github.com/nipreps/mriqc/pull/714>`__)\n  * FIX: Expand usernames on input paths (`#721 <https://github.com/nipreps/mriqc/pull/721>`__)\n  * MAINT: More robust naming of derivatives (related to #661)\n\n* FIX: Do not fail with spurious 4th dimension on T1w (`#738 <https://github.com/nipreps/mriqc/pull/738>`__) @oesteban\n* ENH: Move on to .tsv files (`#737 <https://github.com/nipreps/mriqc/pull/737>`__) @oesteban\n\n0.12.1 (August 13, 2018)\n------------------------\n\n* FIX: ``BIDSLayout`` queries (`#735 <https://github.com/nipreps/mriqc/pull/735>`__)\n\n\n0.12.0 (August 09, 2018)\n------------------------\n\n* FIX: Reduce tSNR memory requirements (`#712 <https://github.com/nipreps/mriqc/pull/712>`__)\n* DOC: Fix typos in IQM documentation (`#725 <https://github.com/nipreps/mriqc/pull/725>`__)\n* PIN: Update MRIQC WebAPI version (`#734 <https://github.com/nipreps/mriqc/pull/734>`__)\n* BUG: Fix missing library in singularity images (`#733 <https://github.com/nipreps/mriqc/pull/733>`__)\n* PIN: nipype 1.1.0, niworkflows (`#726 <https://github.com/nipreps/mriqc/pull/726>`__)\n\n0.11.0 (June 05, 2018)\n----------------------\n\n* RF: Resume external nipype dependency (`#715 <https://github.com/nipreps/mriqc/pull/715>`__)\n\n0.10.6 (May 29, 2018)\n---------------------\n\n* HOTFIX: Bug #659\n\n0.10.5 (May 28, 2018)\n---------------------\n\n* ENH: Report feedback (`#659 <https://github.com/nipreps/mriqc/pull/659>`__)\n\n0.10.4 (March 22, 2018)\n-----------------------\n\n* ENH: Various improvements to reports (`#708 <https://github.com/nipreps/mriqc/pull/708>`__)\n* MAINT: Style revision (`#704 <https://github.com/nipreps/mriqc/pull/704>`__)\n* PIN: pybids 0.5 (`#700 <https://github.com/nipreps/mriqc/pull/700>`__)\n* ENH: Increase FAST memory limits (`#702 <https://github.com/nipreps/mriqc/pull/702>`__)\n\n0.10.3 (February 26, 2018)\n--------------------------\n\n* ENH: Enable T2w metrics uploads (`#696 <https://github.com/nipreps/mriqc/pull/696>`__)\n* PIN: Updating niworkflows (`#698 <https://github.com/nipreps/mriqc/pull/698>`__)\n* DOC: Option ``-o`` is outdated for classifier (`#697 <https://github.com/nipreps/mriqc/pull/697>`__)\n\n0.10.2 (February 15, 2018)\n--------------------------\n\n* ENH: Add warning about mounting relative paths (`#690 <https://github.com/nipreps/mriqc/pull/690>`__)\n* FIX: Sanitize inputs (`#687 <https://github.com/nipreps/mriqc/pull/687>`__)\n* DOC: Fix documentation to use ``--version`` instead of ``-v`` (`#688 <https://github.com/nipreps/mriqc/pull/688>`__)\n\n0.10.1\n------\n\n* FIX: Fixed a bug in reading outputs of ``3dFWHMx`` (`#678 <https://github.com/nipreps/mriqc/pull/678>`__)\n\n0.9.10\n------\n\n* FIX: Updated AFNI to 17.3.03. Resolves errors regarding opening display by ``3dSkullStrip`` (`#669 <https://github.com/nipreps/mriqc/pull/669>`__)\n\n0.9.9\n-----\n\n* ENH: Update nipype to fix ``$DISPLAY`` problem of AFNI's ``3dSkullStrip``\n\n0.9.8\n-----\nWith thanks to Jan Varada (@jvarada) for the session/run filtering.\n\n* ENH: Report recall in cross-validation (requested by reviewer) (`#633 <https://github.com/nipreps/mriqc/pull/633>`__)\n* ENH: Hotfixes to 0.9.7 (`#635 <https://github.com/nipreps/mriqc/pull/635>`__)\n* FIX: Implement filters for session, run and task of BIDS input (`#612 <https://github.com/nipreps/mriqc/pull/612>`__)\n\n0.9.7\n-----\n\n* ENH: Clip outliers in FD and SPIKES group plots (`#593 <https://github.com/nipreps/mriqc/pull/593>`__)\n* ENH: Second revision of the classifier (`#555 <https://github.com/nipreps/mriqc/pull/555>`__):\n  * Set matplotlib plugin to `agg` in docker image\n  * Migrate scalings to sklearn pipelining system\n  * Add Satra's feature selection for RFC (with thanks to S. Ghosh for his suggestion)\n  * Make model selection compatible with sklearn `Pipeline`\n  * Multiclass classification\n  * Add feature selection filter based on Sites prediction (requires pinning to development sklearn-0.19)\n  * Add `RobustLeavePGroupsOut`, replace `RobustGridSearchCV` with the standard `GridSearchCV` of sklearn.\n  * Choice between `RepeatedStratifiedKFold` and `RobustLeavePGroupsOut` in `mriqc_clf`\n  * Write cross-validation results to an `.npz` file.\n* ENH: First revision of the classifier (`#553 <https://github.com/nipreps/mriqc/pull/553>`__):\n  * Add the possibility of changing the scorer function.\n  * Unifize labels for raters in data tables (to `rater_1`)\n  * Add the possibility of setting a custom decision threshold\n  * Write the probabilities in the prediction file\n  * Revised `mriqc_clf` processing flow\n  * Revised labels file for ds030.\n  * Add IQMs for ABIDE and DS030 calculated with MRIQC 0.9.6.\n* ANNOUNCEMENT: Dropped support for Python<-3.4\n* WARNING (`#596 <https://github.com/nipreps/mriqc/pull/596>`__):\n  We have changed the default number of threads for ANTs. Using parallelism with ANTs\n  causes numerical instability on the calculated measures. The most sensitive metrics to this\n  problem are the kurtosis calculations on the intensities of regions and qi_2.\n\n0.9.6\n-----\n\n* ENH: Finished setting up `MRIQC Web API <https://mriqc.nimh.nih.gov>`_\n* ENH: Better error message when --participant_label is set (`#542 <https://github.com/nipreps/mriqc/pull/542>`__)\n* FIX: Allow --load-classifier option to be empty in mriqc_clf (`#544 <https://github.com/nipreps/mriqc/pull/544>`__)\n* FIX: Borked bias estimation derived from Conform (`#541 <https://github.com/nipreps/mriqc/pull/541>`__)\n* ENH: Test against web API 0.3.2 (`#540 <https://github.com/nipreps/mriqc/pull/540>`__)\n* ENH: Change the default Web API address (`#539 <https://github.com/nipreps/mriqc/pull/539>`__)\n* ENH: MRIQCWebAPI: hash fields that may have PI (`#538 <https://github.com/nipreps/mriqc/pull/538>`__)\n* ENH: Added token authorization to MRIQCWebAPI client (`#535 <https://github.com/nipreps/mriqc/pull/535>`__)\n* FIX: Do not mask and antsAffineInitializer twice (`#534 <https://github.com/nipreps/mriqc/pull/534>`__)\n* FIX: Datasets where air (hat) mask is empty (`#533 <https://github.com/nipreps/mriqc/pull/533>`__)\n* ENH: Integration testing for MRIQCWebAPI (`#520 <https://github.com/nipreps/mriqc/pull/520>`__)\n* ENH: Use AFNI to calculate gcor (`#531 <https://github.com/nipreps/mriqc/pull/531>`__)\n* ENH: Refactor derivatives (`#530 <https://github.com/nipreps/mriqc/pull/530>`__)\n* ENH: New bold-IQM: dummy_trs (non-stady state volumes) (`#524 <https://github.com/nipreps/mriqc/pull/524>`__)\n* FIX: Order of BIDS components in IQMs CSV table (`#525 <https://github.com/nipreps/mriqc/pull/525>`__)\n* ENH: Improved logging of mriqc_run (`#526 <https://github.com/nipreps/mriqc/pull/526>`__)\n\n0.9.5\n-----\n\n* ENH: Refactored structural metrics calculation (`#513 <https://github.com/nipreps/mriqc/pull/513>`__)\n* ENH: Calculate rotation mask (`#515 <https://github.com/nipreps/mriqc/pull/515>`__)\n* ENH: Intensity harmonization in the anatomical workflow (`#510 <https://github.com/nipreps/mriqc/pull/510>`__)\n* ENH: Set N4BiasFieldCorrection number of threads (`#506 <https://github.com/nipreps/mriqc/pull/506>`__)\n* ENH: Convert FWHM in pixel units (`#503 <https://github.com/nipreps/mriqc/pull/503>`__)\n* ENH: Add MRIQC client for feature crowdsourcing (`#464 <https://github.com/nipreps/mriqc/pull/464>`__)\n* DOC: Fix functional feature labels in documentation (docs_only) (`#507 <https://github.com/nipreps/mriqc/pull/507>`__)\n* FIX: New implementation for the rPVE feature (normalization, left-tail values) (`#505 <https://github.com/nipreps/mriqc/pull/505>`__)\n* ENH: Parse BIDS selectors (run, task, etc.), improve CLI (`#504 <https://github.com/nipreps/mriqc/pull/504>`__)\n\n\n0.9.4\n-----\n\n* ANNOUNCEMENT: Dropped Python 2 support\n* ENH: Use versioneer to handle versions (`#500 <https://github.com/nipreps/mriqc/pull/500>`__)\n* ENH: Speed up spatial normalization (`#495 <https://github.com/nipreps/mriqc/pull/495>`__)\n* ENH: Resampling of hat mask and TPMs with linear interp (`#498 <https://github.com/nipreps/mriqc/pull/498>`__)\n* TST: Build documentation in CircleCI (`#484 <https://github.com/nipreps/mriqc/pull/484>`__)\n* ENH: Use full-resolution T1w images from ABIDE (`#486 <https://github.com/nipreps/mriqc/pull/486>`__)\n* TST: Parallelize tests (`#493 <https://github.com/nipreps/mriqc/pull/493>`__)\n* TST: Binding /etc/localtime stopped working in docker 1.9.1 (`#492 <https://github.com/nipreps/mriqc/pull/492>`__)\n* TST: Downgrade docker to 1.9.1 in circle (build_only) (`#491 <https://github.com/nipreps/mriqc/pull/491>`__)\n* TST: Check for changes in intermediate nifti files (`#485 <https://github.com/nipreps/mriqc/pull/485>`__)\n* FIX: Erroneous flag --n_proc in CircleCI (`#490 <https://github.com/nipreps/mriqc/pull/490>`__)\n* ENH: Add build_only tag to circle builds (`#488 <https://github.com/nipreps/mriqc/pull/488>`__)\n* ENH: Update Dockerfile (`#482 <https://github.com/nipreps/mriqc/pull/482>`__)\n* FIX: Ignore --profile flag with Linear plugin (`#483 <https://github.com/nipreps/mriqc/pull/483>`__)\n* DOC: Deep revision of the documentation (`#479 <https://github.com/nipreps/mriqc/pull/479>`__)\n* ENH: Minor improvements: SpatialNormalization and segmentation (`#472 <https://github.com/nipreps/mriqc/pull/472>`__)\n* ENH: Fixed typo for neurodebian install via apt-get (`#478 <https://github.com/nipreps/mriqc/pull/478>`__)\n* ENH: Updating fs2gif script (`#465 <https://github.com/nipreps/mriqc/pull/465>`__)\n* ENH: RF: Use niworkflows.interface.SimpleInterface (`#468 <https://github.com/nipreps/mriqc/pull/468>`__)\n* ENH: Add reproducibility of metrics tracking (`#466 <https://github.com/nipreps/mriqc/pull/466>`__)\n\nRelease 0.9.3\n-------------\n\n* ENH: Reafactor of the Dockerfile to improve transparency, reduce size, and enable injecting code in Singularity (`#457 <https://github.com/nipreps/mriqc/pull/457>`__)\n* ENH: Make more the memory consumption estimates of each processing step more conservative to improve robustness (`#456 <https://github.com/nipreps/mriqc/pull/456>`__)\n* FIX: Minor documentation cleanups (`#461 <https://github.com/nipreps/mriqc/pull/461>`__)\n\nRelease 0.9.2\n-------------\n\n* ENH: Optional ICA reports for identifying spatiotemporal artifacts (`#412 <https://github.com/nipreps/mriqc/pull/412>`__)\n* ENH: Add --profile flag (`#435 <https://github.com/nipreps/mriqc/pull/435>`__)\n* ENH: Crashfiles are saved in plain text to improve portability (`#434 <https://github.com/nipreps/mriqc/pull/434>`__)\n* FIX: Fixes EPI mask erosion (`#442 <https://github.com/nipreps/mriqc/pull/442>`__)\n* ENH: Make FSL and AFNI motion correction more comparable by using the same scheme for defining the reference image (`#444 <https://github.com/nipreps/mriqc/pull/444>`__)\n* FIX: Temporarily disabling T1w quality classifier until it can be retrained on new measures (`#447 <https://github.com/nipreps/mriqc/pull/447>`__)\n\nRelease 0.9.1\n-------------\n\n* ENH: Add mriqc version and input image hash to IQMs json file (`#432 <https://github.com/nipreps/mriqc/pull/432>`__)\n* FIX: Affine and warp transforms are now applied in the correct order (`#431 <https://github.com/nipreps/mriqc/pull/431>`__)\n\nRelease 0.9.0-2\n---------------\n\n* ENH: Revise Docker paths (`#429 <https://github.com/nipreps/mriqc/pull/429>`__)\n* FIX: Greedy participant selection (`#426 <https://github.com/nipreps/mriqc/pull/426>`__)\n* FIX: Pin pybids to new version 0.1.0 (`#427 <https://github.com/nipreps/mriqc/pull/427>`__)\n* FIX: Amends sloppy PR #425 (`#428 <https://github.com/nipreps/mriqc/pull/428>`__)\n\nRelease 0.9.0-1\n---------------\n\n* FIX: BOLD reports clipped IQMs after spikes_num (`#425 <https://github.com/nipreps/mriqc/pull/425>`__)\n* FIX: Unicode error writing group reports (`#424 <https://github.com/nipreps/mriqc/pull/424>`__)\n* FIX: Respect Nifi header in fMRI conform node (`#415 <https://github.com/nipreps/mriqc/pull/415>`__)\n* DOC: Deep revision of documentation (#411, #416)\n* ENH: Added sphinx extension to plot workflow graphs (`#411 <https://github.com/nipreps/mriqc/pull/411>`__)\n* FIX: Removed repeated bias correction on anatomical workflows (`#410 <https://github.com/nipreps/mriqc/pull/410>`__)\n* FIX: Race condition in bold workflow when using shared workdir (`#409 <https://github.com/nipreps/mriqc/pull/409>`__)\n* FIX: Tests (#408, #407, #405)\n* FIX: Remove CDN for group level reports (`#406 <https://github.com/nipreps/mriqc/pull/406>`__)\n* FIX: Unused connection, matplotlib segfault (#403, #402)\n* ENH: Skip SpikeFFT detector by default (`#400 <https://github.com/nipreps/mriqc/pull/400>`__)\n* ENH: Use float32 (`#399 <https://github.com/nipreps/mriqc/pull/399>`__)\n* ENH: Spike finder performance improvoments (`#398 <https://github.com/nipreps/mriqc/pull/398>`__)\n* ENH: Basic T2w workflow (`#394 <https://github.com/nipreps/mriqc/pull/394>`__)\n* ENH: Re-enable 3dvolreg (`#390 <https://github.com/nipreps/mriqc/pull/390>`__)\n* ENH: Add T1w classifier (`#389 <https://github.com/nipreps/mriqc/pull/389>`__)\n\nRelease 0.9.0-0\n---------------\n\n* FIX: Remove non-repeatable step from pipeline (`#369 <https://github.com/nipreps/mriqc/pull/369>`__)\n* ENH: Improve group level command line, with more informative output when no IQMs are found for a modality (`#372 <https://github.com/nipreps/mriqc/pull/372>`__)\n* ENH: Make group reports self-contained (`#333 <https://github.com/nipreps/mriqc/pull/333>`__)\n* FIX: New mosaics, based on old ones (#361, #360, #334)\n* FIX: Require numpy>=1.12 to avoid casting problems (`#356 <https://github.com/nipreps/mriqc/pull/356>`__)\n* FIX: Add support for acq and rec tags of BIDS (`#346 <https://github.com/nipreps/mriqc/pull/346>`__)\n* DOC: Documentation updates (`#350 <https://github.com/nipreps/mriqc/pull/350>`__)\n* FIX: pybids compatibility \"No scans were found\" (#340, #347, #342)\n* ENH: Rewrite PYTHONPATH in docker/singularity images (`#345 <https://github.com/nipreps/mriqc/pull/345>`__)\n* ENH: Move metadata onto the bottom of the individual reports (`#332 <https://github.com/nipreps/mriqc/pull/332>`__)\n* ENH: Don't include MNI registration report unlesS --verbose-reports is used (`#362 <https://github.com/nipreps/mriqc/pull/362>`__)\n\n\nRelease 0.8.9\n-------------\n\n* ENH: Added registration svg panel to reports (`#297 <https://github.com/nipreps/mriqc/pull/297>`__)\n\n\nRelease 0.8.8\n-------------\n\n* FIX: Bug translating int16 to uint8 in conform image.\n* FIX: Error in ConformImage interface (`#297 <https://github.com/nipreps/mriqc/pull/297>`__)\n* ENH: Replace BBR by ANTs (#295, #296)\n* FIX: Singularity: user-environment leaking into container (`#293 <https://github.com/nipreps/mriqc/pull/293>`__)\n* ENH: Report failed cases in group report (`#291 <https://github.com/nipreps/mriqc/pull/291>`__)\n* FIX: Brighter anatomical --verbose-reports (`#290 <https://github.com/nipreps/mriqc/pull/290>`__)\n* FIX: X-flip in the mosaics (`#289 <https://github.com/nipreps/mriqc/pull/289>`__)\n* ENH: Show metadata in the individual report (`#288 <https://github.com/nipreps/mriqc/pull/288>`__)\n* ENH: Label in the cutoff threshold - fmriplot (`#287 <https://github.com/nipreps/mriqc/pull/287>`__)\n* ENH: PyBIDS (`#286 <https://github.com/nipreps/mriqc/pull/286>`__)\n* ENH: Simplify tests (`#284 <https://github.com/nipreps/mriqc/pull/284>`__)\n* FIX: MRIQC crashed generating csv files (`#283 <https://github.com/nipreps/mriqc/pull/283>`__)\n* FIX: Bug in setup.py (`#281 <https://github.com/nipreps/mriqc/pull/281>`__)\n* ENH: Makefile (`#280 <https://github.com/nipreps/mriqc/pull/280>`__)\n* FIX: Revision of IQMs (#266, #272, #279)\n* ENH: Deprecation of --nthreads, new flags (`#260 <https://github.com/nipreps/mriqc/pull/260>`__)\n* ENH: Improvements on plots rendering (#254, #257, #258, #267, #268, #269, #270)\n* ENH: FFT detection of spikes (#253, #272)\n* FIX: Labels and links of samples in group plots (`#249 <https://github.com/nipreps/mriqc/pull/249>`__)\n* ENH: Units in group plots (`#242 <https://github.com/nipreps/mriqc/pull/242>`__)\n* FIX: More reliable group level (`#238 <https://github.com/nipreps/mriqc/pull/238>`__)\n* ENH: Add --verbose-reports for fMRI (`#236 <https://github.com/nipreps/mriqc/pull/236>`__)\n* ENH: Migrate functional reports to html (`#232 <https://github.com/nipreps/mriqc/pull/232>`__)\n* ENH: Add 0.2 FD cutoff line (`#231 <https://github.com/nipreps/mriqc/pull/231>`__)\n* ENH: Add AFNI's outlier count to carpet plot confound charts (`#230 <https://github.com/nipreps/mriqc/pull/230>`__)\n\nRelease 0.8.7\n-------------\n\n* ENH: Anatomical Group reports in html (`#227 <https://github.com/nipreps/mriqc/pull/227>`__)\n* ENH: Add kurtosis to summary statistics (`#224 <https://github.com/nipreps/mriqc/pull/224>`__)\n* ENH: New report layout for fMRI, added carpetplot (`#198 <https://github.com/nipreps/mriqc/pull/198>`__)\n* ENH: Anatomical workflow refactor (`#219 <https://github.com/nipreps/mriqc/pull/219>`__).\n\nRelease 0.8.6\n-------------\n\n* [FIX, CRITICAL] Do not chmod in Docker internal scripts\n* FIX: Error creating derivatives folder\n* ENH: Moved MNI spatial normalization to NIworkflows, and made robust.\n* ENH: De-coupled participant and group (reports) levels\n* ENH: Use new FD and DVARs calculations from nipype (`#172 <https://github.com/nipreps/mriqc/pull/172>`__)\n* ENH: Started with python3 compatibility\n* ENH: Added new M2WM measure #158\n* FIX: QI2 is skipped if background intensity is not appropriate (`#147 <https://github.com/nipreps/mriqc/pull/147>`__)\n\nRelease 0.8.5\n-------------\n\n* FIX: Error inverting the T1w-to-MNI warping (`#146 <https://github.com/nipreps/mriqc/pull/146>`__)\n* FIX: TypeError computing DVARS (`#145 <https://github.com/nipreps/mriqc/pull/145>`__)\n* ENH: Plot figure of fitted background chi for QI2 (`#143 <https://github.com/nipreps/mriqc/pull/143>`__)\n* ENH: Move skull-stripping and reorient to NIworkflows (`#142 <https://github.com/nipreps/mriqc/pull/142>`__)\n* FIX: mriqc crashes if no anatomical scans are found (`#141 <https://github.com/nipreps/mriqc/pull/141>`__)\n* DOC: Added acknowledgments to CPAC team members (`#134 <https://github.com/nipreps/mriqc/pull/134>`__)\n* ENH: Use absolute imports (`#133 <https://github.com/nipreps/mriqc/pull/133>`__)\n* FIX: VisibleDeprecationWarning (`#132 <https://github.com/nipreps/mriqc/pull/132>`__)\n* ENH: Provide full FD/DVARS files (`#128 <https://github.com/nipreps/mriqc/pull/128>`__)\n* ENH: Use MCFLIRT to compute motion parameters. AFNI's 3dvolreg now is optional (`#121 <https://github.com/nipreps/mriqc/pull/121>`__)\n* FIX: BIDS trees with anatomical images with different acquisition tokens (`#116 <https://github.com/nipreps/mriqc/pull/116>`__)\n* FIX: BIDS trees with anatomical images with several runs (`#112 <https://github.com/nipreps/mriqc/pull/112>`__)\n* ENH: Options for ANTs normalization: reduced test times (`#124 <https://github.com/nipreps/mriqc/pull/124>`__),\n  and updated options (`#115 <https://github.com/nipreps/mriqc/pull/115>`__)\n\nRelease 0.8.4\n-------------\n\n* ENH: PDF reports now use RST templates and jinja2 (`#109 <https://github.com/nipreps/mriqc/pull/109>`__)\n* FIX: Single-session-multiple-run anatomical files were not correctly located (`#112 <https://github.com/nipreps/mriqc/pull/112>`__)\n\nRelease 0.8.3\n-------------\n\n* DOC: Added examples of the PDF reports (`#107 <https://github.com/nipreps/mriqc/pull/107>`__)\n* FIX: Fixed problems with Python 3 when generating reports.\n\nRelease 0.8.2\n-------------\n\n* ENH: Python 3 compatibility (`#99 <https://github.com/nipreps/mriqc/pull/99>`__)\n* ENH: Add JSON settings file for ANTS (`#95 <https://github.com/nipreps/mriqc/pull/95>`__)\n* ENH: Generate reports automatically if mriqc is run without the -S flag (`#93 <https://github.com/nipreps/mriqc/pull/93>`__)\n* FIX: Revised implementation of QI2 measure (`#90 <https://github.com/nipreps/mriqc/pull/90>`__)\n* AGAVE: Fixed docker image for agave (`#89 <https://github.com/nipreps/mriqc/pull/89>`__)\n* FIX: Problem when generating the air mask with dipy installed (`#88 <https://github.com/nipreps/mriqc/pull/88>`__)\n* ENH: One-session-one-run execution mode (`#85 <https://github.com/nipreps/mriqc/pull/85>`__)\n* AGAVE: Added an agave app description generator (`#84 <https://github.com/nipreps/mriqc/pull/84>`__)\n\nRelease 0.3.0\n-------------\n\n* ENH: Updated CircleCI and Docker to use the version 2.1.0 of ANTs\n  compiled by their developers.\n* ENH: New anatomical workflows to compute the air mask (`#56 <https://github.com/nipreps/mriqc/pull/56>`__)\n\nRelease 0.1.0\n-------------\n\n* FIX: #55\n* ENH: Added rotation of output csv files if they exist\n\nRelease 0.0.2\n-------------\n\n* ENH: Completed migration from QAP\n* ENH: Integration with ReadTheDocs\n* ENH: Submission to PyPi\n\nRelease 0.0.1\n-------------\n\n* Basic mriqc functionality\n"
  },
  {
    "path": "Dockerfile",
    "content": "# MRIQC Docker Container Image distribution\n#\n# MIT License\n#\n# Copyright (c) 2021 The NiPreps Developers\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in all\n# copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n\n# Ubuntu 22.04 LTS - Jammy\nARG BASE_IMAGE=ubuntu:jammy-20240125\n\n#\n# Build wheel\n#\nFROM python:slim AS src\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive \\\n    apt-get install -y --no-install-recommends git\nARG VERSION\nENV SETUPTOOLS_SCM_PRETEND_VERSION=$VERSION\n\nRUN python -m pip install -U pip build\nCOPY . /src\nRUN python -m build /src\n\n# Utilities for downloading packages\nFROM ${BASE_IMAGE} as downloader\n# Bump the date to current to refresh curl/certificates/etc\nRUN echo \"2024.03.18\"\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive \\\n    apt-get install -y --no-install-recommends \\\n                    binutils \\\n                    bzip2 \\\n                    ca-certificates \\\n                    curl \\\n                    unzip && \\\n    apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*\n\n\n# AFNI\nFROM downloader as afni\n# Bump the date to current to update AFNI\nRUN echo \"2024.03.18\"\nRUN mkdir -p /opt/afni-latest \\\n    && curl -fsSL --retry 5 https://afni.nimh.nih.gov/pub/dist/tgz/linux_openmp_64.tgz \\\n    | tar -xz -C /opt/afni-latest --strip-components 1 \\\n    --exclude \"linux_openmp_64/*.gz\" \\\n    --exclude \"linux_openmp_64/funstuff\" \\\n    --exclude \"linux_openmp_64/shiny\" \\\n    --exclude \"linux_openmp_64/afnipy\" \\\n    --exclude \"linux_openmp_64/lib/RetroTS\" \\\n    --exclude \"linux_openmp_64/lib_RetroTS\" \\\n    --exclude \"linux_openmp_64/meica.libs\" \\\n    # Keep only what we use\n    && find /opt/afni-latest -type f -not \\( \\\n            -name \"3dAutomask\" \\\n        -or -name \"3dcalc\" \\\n        -or -name \"3dFWHMx\" \\\n        -or -name \"3dinfo\" \\\n        -or -name \"3dmaskave\" \\\n        -or -name \"3dSkullStrip\" \\\n        -or -name \"3dTnorm\" \\\n        -or -name \"3dToutcount\" \\\n        -or -name \"3dTqual\" \\\n        -or -name \"3dTshift\" \\\n        -or -name \"3dTstat\" \\\n        -or -name \"3dUnifize\" \\\n        -or -name \"3dvolreg\" \\\n        -or -name \"@compute_gcor\" \\\n        -or -name \"afni\" \\\n       \\) -delete\n\n# Use Ubuntu 20.04 LTS\nFROM nipreps/miniconda:py39_2403.0\n\nARG DEBIAN_FRONTEND=noninteractive\nENV LD_LIBRARY_PATH=\"/usr/lib/x86_64-linux-gnu:${CONDA_PATH}/lib\"\nENV CONDA_PATH=\"/opt/conda\"\n\n# Configure PPAs for libpng12 and libxp6\nRUN GNUPGHOME=/tmp gpg --keyserver hkps://keyserver.ubuntu.com --no-default-keyring --keyring /usr/share/keyrings/linuxuprising.gpg --recv 0xEA8CACC073C3DB2A \\\n    && GNUPGHOME=/tmp gpg --keyserver hkps://keyserver.ubuntu.com --no-default-keyring --keyring /usr/share/keyrings/zeehio.gpg --recv 0xA1301338A3A48C4A \\\n    && echo \"deb [signed-by=/usr/share/keyrings/linuxuprising.gpg] https://ppa.launchpadcontent.net/linuxuprising/libpng12/ubuntu jammy main\" > /etc/apt/sources.list.d/linuxuprising.list \\\n    && echo \"deb [signed-by=/usr/share/keyrings/zeehio.gpg] https://ppa.launchpadcontent.net/zeehio/libxp/ubuntu jammy main\" > /etc/apt/sources.list.d/zeehio.list\n\n# Dependencies for AFNI; requires a discontinued multiarch-support package from bionic (18.04)\nRUN apt-get update -qq \\\n    && apt-get install -y -q --no-install-recommends \\\n           ed \\\n           gsl-bin \\\n           libglib2.0-0 \\\n           libglu1-mesa-dev \\\n           libglw1-mesa \\\n           libgomp1 \\\n           libjpeg62 \\\n           libpng12-0 \\\n           libxm4 \\\n           libxp6 \\\n           netpbm \\\n           tcsh \\\n           xfonts-base \\\n           xvfb \\\n    && curl -sSL --retry 5 -o /tmp/multiarch.deb http://archive.ubuntu.com/ubuntu/pool/main/g/glibc/multiarch-support_2.27-3ubuntu1.5_amd64.deb \\\n    && dpkg -i /tmp/multiarch.deb \\\n    && rm /tmp/multiarch.deb \\\n    && apt-get install -f \\\n    && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \\\n    && gsl2_path=\"$(find / -name 'libgsl.so.19' || printf '')\" \\\n    && if [ -n \"$gsl2_path\" ]; then \\\n         ln -sfv \"$gsl2_path\" \"$(dirname $gsl2_path)/libgsl.so.0\"; \\\n    fi \\\n    && ldconfig\n\n# Install AFNI\nENV AFNI_DIR=\"/opt/afni\"\nCOPY --from=afni /opt/afni-latest ${AFNI_DIR}\nENV PATH=\"${AFNI_DIR}:$PATH\" \\\n    AFNI_IMSAVE_WARNINGS=\"NO\" \\\n    AFNI_MODELPATH=\"${AFNI_DIR}/models\" \\\n    AFNI_TTATLAS_DATASET=\"${AFNI_DIR}/atlases\" \\\n    AFNI_PLUGINPATH=\"${AFNI_DIR}/plugins\"\n\n# Install AFNI's dependencies\nRUN micromamba install -n base -c conda-forge \"ants=2.5\" \\\n            && sync \\\n\t    && micromamba clean -afy; sync \\\n\t    && ldconfig\n\n# Unless otherwise specified each process should only use one thread - nipype\n# will handle parallelization\nENV MKL_NUM_THREADS=1 \\\n    OMP_NUM_THREADS=1 \\\n    NUMEXPR_MAX_THREADS=1\n\nCOPY --from=freesurfer/synthstrip@sha256:f19578e5f033f2c707fa66efc8b3e11440569facb46e904b45fd52f1a12beb8b /freesurfer/models/synthstrip.1.pt /opt/freesurfer/models/synthstrip.1.pt\n\nENV FREESURFER_HOME=/opt/freesurfer\n\nRUN apt update && apt install --no-install-recommends -y libtiff5 libpng16-16\n\nCOPY --from=mrtrix3/mrtrix3:3.0.4 /opt/mrtrix3/bin/dwidenoise /usr/local/bin\nCOPY --from=mrtrix3/mrtrix3:3.0.4 /opt/mrtrix3/lib/libmrtrix.so /usr/local/lib\n\n# Container Sentinel\nENV IS_DOCKER_8395080871=1\n\n# Create a shared $HOME directory\nRUN useradd -m -s /bin/bash -G users mriqc\nWORKDIR /home/mriqc\nENV HOME=\"/home/mriqc\"\n\n# Pacify datalad\nRUN git config --global user.name \"NiPreps - MRIQC\" \\\n    && git config --global user.email \"nipreps@gmail.com\"\n\nRUN micromamba shell init -s bash\nENV PATH=\"${CONDA_PATH}/bin:$PATH\" \\\n    CPATH=\"${CONDA_PATH}/include:$CPATH\" \\\n    LD_LIBRARY_PATH=\"${CONDA_PATH}/lib:$LD_LIBRARY_PATH\"\n\n# Refresh linked libraries\nRUN echo \"${CONDA_PATH}/lib\" > /etc/ld.so.conf.d/conda.conf \\\n    && ldconfig\n# Installing dev requirements (packages that are not in pypi)\nWORKDIR /src/\n# Precaching atlases\nRUN python -c \"from templateflow import api as tfapi; \\\n\t       tfapi.get('MNI152NLin2009cAsym', resolution=[1, 2], suffix=['T1w', 'T2w'], desc=None); \\\n\t       tfapi.get('MNI152NLin2009cAsym', resolution=[1, 2], suffix='mask',\\\n\t\t\t desc=['brain', 'head']); \\\n\t       tfapi.get('MNI152NLin2009cAsym', resolution=1, suffix='dseg', desc='carpet'); \\\n\t       tfapi.get('MNI152NLin2009cAsym', resolution=1, suffix='probseg',\\\n\t\t\t label=['CSF', 'GM', 'WM']);\\\n\t       tfapi.get('MNI152NLin2009cAsym', resolution=[1, 2], suffix='boldref')\"\n\n# Installing MRIQC\nCOPY --from=src /src/dist/*.whl .\nRUN pip install --no-cache-dir $( ls *.whl )[container,rodents,test]\n\nRUN find $HOME -type d -exec chmod go=u {} + && \\\n    find $HOME -type f -exec chmod go=u {} + && \\\n    rm -rf $HOME/.npm $HOME/.conda $HOME/.empty\n\n# Best practices\nRUN ldconfig\n\n# Update version\nRUN export VERSION=$(python -m mriqc --version | awk '{print $NF}') \\\n    && echo \"VERSION=$VERSION\" >> /etc/environment\n\nWORKDIR /tmp/\n\n# Run mriqc by default\nENTRYPOINT [\"/opt/conda/bin/mriqc\"]\nARG BUILD_DATE\nARG VCS_REF\nLABEL org.label-schema.build-date=$BUILD_DATE \\\n      org.label-schema.name=\"MRIQC\" \\\n      org.label-schema.description=\"MRIQC - Automated Quality Control and visual reports for Quality Assessment of structural (T1w, T2w) and functional MRI of the brain\" \\\n      org.label-schema.url=\"https://mriqc.readthedocs.io\" \\\n      org.label-schema.vcs-ref=$VCS_REF \\\n      org.label-schema.vcs-url=\"https://github.com/nipreps/mriqc\" \\\n      org.label-schema.version=$VERSION \\\n      org.label-schema.schema-version=\"1.0\"\n"
  },
  {
    "path": "Dockerfile_devel",
    "content": "FROM nipreps/mriqc:latest\nCOPY . /src/mriqc\nARG VERSION\nRUN export SETUPTOOLS_SCM_PRETEND_VERSION=$VERSION && pip install -e /src/mriqc[all]\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2020 The NiPreps Developers\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "#documentation\nrecursive-exclude .circleci/ *\nrecursive-exclude .github/ *\nrecursive-exclude .git/ *\nrecursive-exclude .maint/ *\nrecursive-exclude docker/ *\nrecursive-exclude docs/ *\nrecursive-exclude test/ *\n\nexclude .*\nexclude Dockerfile\nexclude Dockerfile_devel\nexclude Makefile\n\n#data\nrecursive-include mriqc/data *\n"
  },
  {
    "path": "Makefile",
    "content": "# Basic actions\n\nTEST_PATH = ./\nMRIQC_VERSION = latest\n\n\n.PHONY: clean-pyc\nclean-pyc:\n\t\tfind . -name '__pycache__' -type d -exec rm -r {} +\n\t\tfind . -name '*.pyc' -exec rm --force {} +\n\t\tfind . -name '*.pyo' -exec rm --force {} +\n\t\tfind . -name '*~' -exec rm --force  {} +\n\n.PHONY: clean-build\nclean-build:\n\t\trm --force --recursive build/\n\t\trm --force --recursive dist/\n\t\trm --force --recursive *.egg-info\n\t\trm --force --recursive src/\n\n.PHONY: tag\ntag:\n\t\tgit tag -a $(VERSION) -m \"Version ${VERSION}\"\n\t\tgit push origin $(VERSION)\n\t\tgit push upstream $(VERSION)\n\nlint:\n\t\tpylint ./mriqc/\n\n.PHONY: test\ntest: clean-pyc\n\t\tpy.test --ignore=src/ --verbose $(TEST_PATH)\n\ndist: clean-build clean-pyc\n\t\tpython setup.py sdist\n\n.PHONY: docker\ndocker:\n\t\tdocker build -t nipreps/mriqc:$(MRIQC_VERSION) \\\n\t\t\t--build-arg BUILD_DATE=`date -u +\"%Y-%m-%dT%H:%M:%SZ\"` \\\n\t\t\t--build-arg VCS_REF=`git rev-parse --short HEAD` \\\n\t\t\t--build-arg VERSION=`python setup.py --version` .\n\n.PHONY: release\nrelease: clean-build tag docker\n\t\tpython setup.py sdist\n\t\ttwine upload dist/*\n\nsingularity: docker\n\tmkdir -p build/singularity\n\tdocker run --privileged -ti --rm  \\\n    \t-v /var/run/docker.sock:/var/run/docker.sock \\\n    \t-v $(shell pwd)/build/singularity:/output \\\n    \tsingularityware/docker2singularity \\\n    \tnipreps/mriqc:$(MRIQC_VERSION)\n\n"
  },
  {
    "path": "README.rst",
    "content": "mriqc: image quality metrics for quality assessment of MRI\n==========================================================\n\n|DOI| |Zenodo| |Package| |Pythons| |DevStatus| |PackageBuild| |License| |Documentation| |CircleCI| |EOSS|\n\nMRIQC extracts no-reference IQMs (image quality metrics) from\nstructural (T1w and T2w), functional and diffusion MRI (magnetic resonance imaging)\ndata.\n\nMRIQC is an open-source project, developed under the following\nsoftware engineering principles:\n\n#. **Modularity and integrability**: MRIQC implements a\n   `nipype <https://nipype.readthedocs.io>`_ workflow to integrate modular\n   sub-workflows that rely upon third party software toolboxes such as\n   ANTs and AFNI.\n\n#. **Minimal preprocessing**: the MRIQC workflows should be as minimal\n   as possible to estimate the IQMs on the original data or their minimally\n   processed derivatives.\n\n#. **Interoperability and standards**: MRIQC follows the the `brain imaging data structure\n   (BIDS) <https://bids.neuroimaging.io>`_, and it adopts the `BIDS-App\n   <https://bids-apps.neuroimaging.io>`_ standard.\n\n#. **Reliability and robustness**: the software undergoes frequent vetting sprints\n   by testing its robustness against data variability (acquisition parameters,\n   physiological differences, etc.) using images from `OpenfMRI <https://openfmri.org>`_.\n   Its reliability is permanently checked and maintained with\n   `CircleCI <https://circleci.com/gh/nipreps/mriqc>`_.\n\nCitation\n--------\n.. topic:: **When using MRIQC, please include the following citation:**\n\n    Esteban O, Birman D, Schaer M, Koyejo OO, Poldrack RA, Gorgolewski KJ;\n    *MRIQC: Advancing the Automatic Prediction of Image Quality in MRI from Unseen Sites*;\n    PLOS ONE 12(9):e0184661; doi:`10.1371/journal.pone.0184661 <https://doi.org/10.1371/journal.pone.0184661>`_.\n\nSupport and communication\n-------------------------\nThe documentation of this project is found here: https://mriqc.readthedocs.io/.\n\nUsers can get help using the `mriqc-users google group <https://groups.google.com/forum/#!forum/mriqc-users>`_.\n\nAll bugs, concerns and enhancement requests for this software can be submitted here:\nhttps://github.com/nipreps/mriqc/issues.\n\nDevelopment\n-----------\nA local development build based on the latest docker build of MRIQC can be built with this command run from \nthe root of this repository::\n\n    docker build -f Dockerfile_devel -t mriqc_devel .\n\nTo test changes the local source code will need to be mounted into the development container::\n\n    docker run --rm -v .:/src/mriqc mriqc_devel\n\nNew Python dependencies can be added in ``pyproject.toml`` under ``dependencies``.\nAny time a dependency is changed or added there the docker image will need to be rebuilt\nusing the above ``docker build`` command.\n\nLicense information\n-------------------\n*MRIQC* adheres to the\n`general licensing guidelines <https://www.nipreps.org/community/licensing/>`__\nof the *NiPreps framework*.\n\n*MRIQC* originally derives from, and hence is heavily influenced by, the\n`PCP Quality Assessment Protocol\n<http://preprocessed-connectomes-project.org/quality-assessment-protocol/>`__.\nPlease check the ``NOTICE`` file for further information.\n\nLicense\n~~~~~~~\nCopyright (c) 2021, the *NiPreps* Developers.\n\nAs of the 21.0.x pre-release and release series, *MRIQC* is\nlicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n`http://www.apache.org/licenses/LICENSE-2.0\n<http://www.apache.org/licenses/LICENSE-2.0>`__.\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\nAcknowledgements\n----------------\nThis work is steered and maintained by the `NiPreps Community <https://www.nipreps.org>`__.\nThe development of this resource was supported by\nthe Laura and John Arnold Foundation (RAP and KJG),\nthe NIBIB (R01EB020740, SSG; 1P41EB019936-01A1SSG, YOH),\nthe NIMH (RF1MH121867, RAP, OE; R24MH114705 and R24MH117179, RAP; 1RF1MH121885 SSG),\nNINDS (U01NS103780, RAP), and NSF (CRCNS 1912266, YOH).\nOE acknowledges financial support from the SNSF Ambizione project\n“*Uncovering the interplay of structure, function, and dynamics of\nbrain connectivity using MRI*” (grant number\n`PZ00P2_185872 <http://p3.snf.ch/Project-185872>`__).\n\n.. topic:: **Thanks**\n\n    * The QAP developers (C. Craddock, S. Giavasis, D. Clark, Z. Shezhad, and J.\n      Pellman) for the initial base of code which MRIQC was forked from.\n    * W Triplett and CA Moodie for their initial contributions with bugfixes and documentation, and\n    * J Varada for his contributions on the source code.\n\n\n.. |DOI| image:: https://img.shields.io/badge/doi-10.1371%2Fjournal.pone.0184661-blue.svg\n   :target: https://doi.org/10.1371/journal.pone.0184661\n.. |Zenodo| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.2630889.svg\n   :target: https://doi.org/10.5281/zenodo.2630889\n.. |Package| image:: https://img.shields.io/pypi/v/mriqc.svg\n   :target: https://pypi.python.org/pypi/mriqc/\n.. |Pythons| image:: https://img.shields.io/pypi/pyversions/mriqc.svg\n   :target: https://pypi.python.org/pypi/mriqc/\n.. |DevStatus| image:: https://img.shields.io/pypi/status/mriqc.svg\n   :target: https://pypi.python.org/pypi/mriqc/\n.. |PackageBuild| image:: https://github.com/nipreps/mriqc/actions/workflows/pythonpackage.yml/badge.svg\n   :target: https://github.com/nipreps/mriqc/actions/workflows/pythonpackage.yml\n.. |License| image:: https://img.shields.io/pypi/l/mriqc.svg\n   :target: https://pypi.python.org/pypi/mriqc/\n.. |Documentation| image:: https://readthedocs.org/projects/mriqc/badge/?version=latest\n   :target: http://mriqc.readthedocs.io/en/latest/?badge=latest\n.. |CircleCI| image:: https://circleci.com/gh/nipreps/mriqc/tree/master.svg?style=shield\n   :target: https://circleci.com/gh/nipreps/mriqc/tree/master\n.. |EOSS| image:: https://chanzuckerberg.github.io/open-science/badges/CZI-EOSS.svg\n  :target: https://czi.co/EOSS\n  :alt: CZI's Essential Open Source Software for Science\n"
  },
  {
    "path": "docker/files/neurodebian.gpg",
    "content": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v1\n\n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\n=fAJZ\n-----END PGP PUBLIC KEY BLOCK-----\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = _build\nPYTHONPATH    = $(PWD)\n\n# User-friendly check for sphinx-build\nifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)\n$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)\nendif\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source\n# the i18n builder cannot share the environment and doctrees with the others\nI18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source\n\n.PHONY: help\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  applehelp  to make an Apple Help Book\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  latexpdfja to make LaTeX files and run them through platex/dvipdfmx\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  texinfo    to make Texinfo files\"\n\t@echo \"  info       to make Texinfo files and run them through makeinfo\"\n\t@echo \"  gettext    to make PO message catalogs\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  xml        to make Docutils-native XML files\"\n\t@echo \"  pseudoxml  to make pseudoxml-XML files for display purposes\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\t@echo \"  coverage   to run coverage check of the documentation (if enabled)\"\n\n.PHONY: clean\nclean:\n\trm -rf $(BUILDDIR)/*\n\n.PHONY: html\nhtml:\n\tPYTHONPATH=$(PYTHONPATH) $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\n.PHONY: dirhtml\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\n.PHONY: singlehtml\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\n.PHONY: pickle\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\n.PHONY: json\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\n.PHONY: htmlhelp\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\n.PHONY: qthelp\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/mriqc.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/mriqc.qhc\"\n\n.PHONY: applehelp\napplehelp:\n\t$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp\n\t@echo\n\t@echo \"Build finished. The help book is in $(BUILDDIR)/applehelp.\"\n\t@echo \"N.B. You won't be able to view it unless you put it in\" \\\n\t      \"~/Library/Documentation/Help or install it in your application\" \\\n\t      \"bundle.\"\n\n.PHONY: devhelp\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/mriqc\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mriqc\"\n\t@echo \"# devhelp\"\n\n.PHONY: epub\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\n.PHONY: latex\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\n.PHONY: latexpdf\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\n.PHONY: latexpdfja\nlatexpdfja:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through platex and dvipdfmx...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\n.PHONY: text\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\n.PHONY: man\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\n.PHONY: texinfo\ntexinfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo\n\t@echo \"Build finished. The Texinfo files are in $(BUILDDIR)/texinfo.\"\n\t@echo \"Run \\`make' in that directory to run these through makeinfo\" \\\n\t      \"(use \\`make info' here to do that automatically).\"\n\n.PHONY: info\ninfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo \"Running Texinfo files through makeinfo...\"\n\tmake -C $(BUILDDIR)/texinfo info\n\t@echo \"makeinfo finished; the Info files are in $(BUILDDIR)/texinfo.\"\n\n.PHONY: gettext\ngettext:\n\t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale\n\t@echo\n\t@echo \"Build finished. The message catalogs are in $(BUILDDIR)/locale.\"\n\n.PHONY: changes\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\n.PHONY: linkcheck\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\n.PHONY: doctest\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n\n.PHONY: coverage\ncoverage:\n\t$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage\n\t@echo \"Testing of coverage in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/coverage/python.txt.\"\n\n.PHONY: xml\nxml:\n\t$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml\n\t@echo\n\t@echo \"Build finished. The XML files are in $(BUILDDIR)/xml.\"\n\n.PHONY: pseudoxml\npseudoxml:\n\t$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml\n\t@echo\n\t@echo \"Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml.\"\n"
  },
  {
    "path": "docs/notebooks/.gitignore",
    "content": ".ipynb_checkpoints/\nABIDE-BIDS/\ndata/\nexample_artifacts_dataset/\nout/\ntemp/\n*.svg\n"
  },
  {
    "path": "docs/notebooks/MRIQC Web API.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Querying the MRIQC Web API\\n\",\n    \"\\n\",\n    \"This notebook shows how the web-API can be leveraged to analyze the image quality metrics (IQMs) that have been extracted with MRIQC\\n\",\n    \"\\n\",\n    \"This notebook is a derivative work of https://gist.github.com/chrisfilo/eccdb8b98f8e74d24a3395a49fbadf03\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import json\\n\",\n    \"import multiprocessing as mp\\n\",\n    \"import urllib.request\\n\",\n    \"from json import load\\n\",\n    \"\\n\",\n    \"import numpy as np\\n\",\n    \"import pandas as pd\\n\",\n    \"import pylab as plt\\n\",\n    \"import seaborn as sns\\n\",\n    \"from pandas.io.json import json_normalize\\n\",\n    \"\\n\",\n    \"%matplotlib inline\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Preparation\\n\",\n    \"\\n\",\n    \"Let's define a function that will query the appropriate endpoint and a helper function to plot some distributions (at the bottom of this notebook).\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"def get_iqms(modality, versions=None, software='mriqc'):\\n\",\n    \"    \\\"\\\"\\\"\\n\",\n    \"    Grab all iqms for the given modality and the list of versions\\n\",\n    \"    \\\"\\\"\\\"\\n\",\n    \"    url_root = 'https://mriqc.nimh.nih.gov/api/v1/{modality}?{query}'\\n\",\n    \"    page = 1\\n\",\n    \"    dfs = []\\n\",\n    \"\\n\",\n    \"    if versions is None:\\n\",\n    \"        versions = ['*']\\n\",\n    \"\\n\",\n    \"    for version in versions:\\n\",\n    \"        while True:\\n\",\n    \"            query = []\\n\",\n    \"\\n\",\n    \"            if software is not None:\\n\",\n    \"                query.append(f'\\\"provenance.software\\\":\\\"{software}\\\"')\\n\",\n    \"\\n\",\n    \"            if version != '*':\\n\",\n    \"                query.append(f'\\\"provenance.version\\\":\\\"{version}\\\"')\\n\",\n    \"\\n\",\n    \"            where = ','.join(query)\\n\",\n    \"            page_url = url_root.format(modality=modality, query=f'where={where}&page={page}')\\n\",\n    \"            with urllib.request.urlopen(page_url) as url:\\n\",\n    \"                data = json.loads(url.read().decode())\\n\",\n    \"                dfs.append(json_normalize(data['_items']))\\n\",\n    \"                if 'next' not in data['_links'].keys():\\n\",\n    \"                    break\\n\",\n    \"                else:\\n\",\n    \"                    page += 1\\n\",\n    \"\\n\",\n    \"    # Compose a pandas dataframe\\n\",\n    \"    return pd.concat(dfs, ignore_index=True)\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"def plot_measure(data, xlabel=None, label=None, ax=None, min=None, max=None):\\n\",\n    \"    \\\"\\\"\\\"\\n\",\n    \"    Distribution plot of a given measure\\n\",\n    \"    \\\"\\\"\\\"\\n\",\n    \"    sns.distplot(data, ax=ax, label=label)\\n\",\n    \"\\n\",\n    \"    if xlabel is not None:\\n\",\n    \"        ax.set_xlabel(xlabel)\\n\",\n    \"\\n\",\n    \"    if min is None:\\n\",\n    \"        min = np.percentile(data, 0.5)\\n\",\n    \"\\n\",\n    \"    if max is None:\\n\",\n    \"        max = np.percentile(data, 99.5)\\n\",\n    \"    ax.set_xlim((min, max))\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Fetch IQMs\\n\",\n    \"\\n\",\n    \"Let's fetch IQMs for the two principal modalities of MRIQC, T1-weighted images and BOLD-fMRI. Filter out repeated images to obtain unique records.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"# T1\\n\",\n    \"df_t1w = get_iqms('T1w', software=None)\\n\",\n    \"df_t1w_unique = df_t1w.drop_duplicates(subset=['provenance.md5sum'])\\n\",\n    \"\\n\",\n    \"# BOLD\\n\",\n    \"df_bold = get_iqms('bold')\\n\",\n    \"df_bold_unique = df_bold.drop_duplicates(subset=['provenance.md5sum'])\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Evolution of IQMs submission\\n\",\n    \"\\n\",\n    \"This code generates Figure 3A of the abstract\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\\nQmCC\\n\",\n      \"text/plain\": [\n       \"<matplotlib.figure.Figure at 0x7f40532a60f0>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"import datetime\\n\",\n    \"\\n\",\n    \"import matplotlib.dates as mdates\\n\",\n    \"from dateutil import parser\\n\",\n    \"\\n\",\n    \"dates_t1w = [parser.parse(d) for d in df_t1w['_created'].values]\\n\",\n    \"dates_t1w.sort()\\n\",\n    \"\\n\",\n    \"dates_t1w_u = [parser.parse(d) for d in df_t1w_unique['_created'].values]\\n\",\n    \"dates_t1w_u.sort()\\n\",\n    \"# mindate = dates_t1w[0]\\n\",\n    \"ax = plt.subplot(111)\\n\",\n    \"ax.plot(dates_t1w, list(range(1, len(dates_t1w) + 1)), label='total')\\n\",\n    \"ax.plot(dates_t1w_u, list(range(1, len(dates_t1w_u) + 1)), label='unique')\\n\",\n    \"ax.set_title('Number of T1w records in database')\\n\",\n    \"ax.legend()\\n\",\n    \"\\n\",\n    \"plt.savefig('fig03a-0.svg', bbox_inches='tight', transparent=False, pad_inches=0)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 5,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\\n\",\n      \"text/plain\": [\n       \"<matplotlib.figure.Figure at 0x7f4053215eb8>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"dates_bold = [parser.parse(d) for d in df_bold['_created'].values]\\n\",\n    \"dates_bold.sort()\\n\",\n    \"dates_bold_u = [parser.parse(d) for d in df_bold_unique['_created'].values]\\n\",\n    \"dates_bold_u.sort()\\n\",\n    \"# mindate = dates_t1w[0]\\n\",\n    \"ax = plt.subplot(111)\\n\",\n    \"ax.plot(dates_bold, list(range(1, len(dates_bold) + 1)), label='total')\\n\",\n    \"ax.plot(dates_bold_u, list(range(1, len(dates_bold_u) + 1)), label='unique')\\n\",\n    \"ax.set_title('Number of BOLD records in database')\\n\",\n    \"ax.legend()\\n\",\n    \"plt.savefig('fig03a-1.svg', bbox_inches='tight', transparent=False, pad_inches=0)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Playing with T1w IQMs\\n\",\n    \"\\n\",\n    \"Let's plot some of the IQMs for the T1w modality. First, let's check the names of the IQMs. These measures are explained in the documentation (http://mriqc.readthedocs.io/en/stable/iqms/t1w.html)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"cjv,cnr,efc,fber,fwhm_avg,fwhm_x,fwhm_y,fwhm_z,icvs_csf,icvs_gm,icvs_wm,inu_med,inu_range,qi_1,qi_2,rpve_csf,rpve_gm,rpve_wm,size_x,size_y,size_z,snr_csf,snr_gm,snr_total,snr_wm,snrd_csf,snrd_gm,snrd_total,snrd_wm,spacing_x,spacing_y,spacing_z,summary_bg_k,summary_bg_mad,summary_bg_mean,summary_bg_median,summary_bg_n,summary_bg_p05,summary_bg_p95,summary_bg_stdv,summary_csf_k,summary_csf_mad,summary_csf_mean,summary_csf_median,summary_csf_n,summary_csf_p05,summary_csf_p95,summary_csf_stdv,summary_gm_k,summary_gm_mad,summary_gm_mean,summary_gm_median,summary_gm_n,summary_gm_p05,summary_gm_p95,summary_gm_stdv,summary_wm_k,summary_wm_mad,summary_wm_mean,summary_wm_median,summary_wm_n,summary_wm_p05,summary_wm_p95,summary_wm_stdv,tpm_overlap_csf,tpm_overlap_gm,tpm_overlap_wm,wm2max\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"print(\\n\",\n    \"    ','.join(\\n\",\n    \"        [\\n\",\n    \"            line\\n\",\n    \"            for line in df_t1w.columns\\n\",\n    \"            if not line.startswith('_')\\n\",\n    \"            and not line.startswith('bids_meta')\\n\",\n    \"            and not line.startswith('provenance')\\n\",\n    \"        ]\\n\",\n    \"    )\\n\",\n    \")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 7,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\\n\",\n      \"text/plain\": [\n       \"<matplotlib.figure.Figure at 0x7f409c5b0978>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"f, ax = plt.subplots(1, 5, figsize=(25, 5))\\n\",\n    \"plot_measure(df_t1w_unique.cjv, xlabel='Coefficient of joint variation (CJV)', ax=ax[0])\\n\",\n    \"plot_measure(df_t1w_unique.cnr, xlabel='Contrast-to-noise ratio (CNR)', ax=ax[1])\\n\",\n    \"plot_measure(\\n\",\n    \"    df_t1w_unique.snr_wm,\\n\",\n    \"    xlabel='Signal-to-noise ratio estimated on the white-matter (SNR)',\\n\",\n    \"    ax=ax[2],\\n\",\n    \")\\n\",\n    \"plot_measure(df_t1w_unique.fwhm_avg, xlabel='Smoothness (FWHM)', ax=ax[3])\\n\",\n    \"plot_measure(df_t1w_unique.wm2max, xlabel='WM-to-max intensity ratio (WM2MAX)', ax=ax[4])\\n\",\n    \"plt.suptitle('Distributions of some IQMs extracted from T1-weighted MRI')\\n\",\n    \"plt.savefig('fig03b-0.svg', bbox_inches='tight', transparent=False, pad_inches=0)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Playing with BOLD IQMs\\n\",\n    \"\\n\",\n    \"Let's plot some of the IQMs for the BOLD modality. First, let's check the names of the IQMs. These measures are explained in the documentation (http://mriqc.readthedocs.io/en/stable/iqms/bold.html)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 8,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"aor,aqi,dummy_trs,dvars_nstd,dvars_std,dvars_vstd,efc,fber,fd_mean,fd_num,fd_perc,fwhm_avg,fwhm_x,fwhm_y,fwhm_z,gcor,gsr_x,gsr_y,size_t,size_x,size_y,size_z,snr,spacing_tr,spacing_x,spacing_y,spacing_z,summary_bg_k,summary_bg_mad,summary_bg_mean,summary_bg_median,summary_bg_n,summary_bg_p05,summary_bg_p95,summary_bg_stdv,summary_fg_k,summary_fg_mad,summary_fg_mean,summary_fg_median,summary_fg_n,summary_fg_p05,summary_fg_p95,summary_fg_stdv,tsnr\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"print(\\n\",\n    \"    ','.join(\\n\",\n    \"        [\\n\",\n    \"            line\\n\",\n    \"            for line in df_bold.columns\\n\",\n    \"            if not line.startswith('_')\\n\",\n    \"            and not line.startswith('bids_meta')\\n\",\n    \"            and not line.startswith('provenance')\\n\",\n    \"        ]\\n\",\n    \"    )\\n\",\n    \")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 9,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\\n\",\n      \"text/plain\": [\n       \"<matplotlib.figure.Figure at 0x7f4053b50588>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"f, ax = plt.subplots(1, 5, figsize=(25, 5))\\n\",\n    \"plot_measure(\\n\",\n    \"    df_bold_unique[df_bold_unique.fd_mean < 10].fd_mean,\\n\",\n    \"    xlabel='Framewise Displacement (mm)',\\n\",\n    \"    max=2,\\n\",\n    \"    ax=ax[0],\\n\",\n    \")\\n\",\n    \"plot_measure(df_bold_unique[df_bold_unique.dvars_nstd < 100].dvars_nstd, xlabel='DVARS', ax=ax[1])\\n\",\n    \"plot_measure(df_bold_unique.gcor, xlabel='Global correlation', ax=ax[2])\\n\",\n    \"plot_measure(df_bold_unique.gsr_x, label='x-axis', ax=ax[3])\\n\",\n    \"plot_measure(df_bold_unique.gsr_y, xlabel='Ghost-to-signal ratio (GSR)', label='y-axis', ax=ax[3])\\n\",\n    \"ax[3].legend()\\n\",\n    \"plot_measure(df_bold_unique.tsnr, xlabel='Temporal SNR (tSNR)', ax=ax[4])\\n\",\n    \"plt.suptitle('Distributions of some IQMs extracted from BOLD fMRI')\\n\",\n    \"plt.savefig(\\n\",\n    \"    'fig03b-1.png', bbox_inches='tight', transparent=False, pad_inches=0, facecolor='white'\\n\",\n    \")\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python 3\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.6.1\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "docs/notebooks/Paper-v1.0.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"%matplotlib inline\\n\",\n    \"%load_ext autoreload\\n\",\n    \"%autoreload 2\\n\",\n    \"import os.path as op\\n\",\n    \"\\n\",\n    \"import matplotlib.pyplot as plt\\n\",\n    \"import numpy as np\\n\",\n    \"import pandas as pd\\n\",\n    \"import seaborn as sns\\n\",\n    \"\\n\",\n    \"sns.set(style='whitegrid')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"from mriqc.classifier import data as mcd\\n\",\n    \"\\n\",\n    \"abide, _ = mcd.read_dataset(x_path, y_path, rate_label='rater_1')\\n\",\n    \"sites = sorted(set(abide.site.values.ravel()))\\n\",\n    \"\\n\",\n    \"fmt = r'{site} & \\\\pixmat{{{size[0]:d}$\\\\pm${sr[0]:d}}}{{{size[1]:d}$\\\\pm${sr[1]:d}}}{{{size[2]:d}$\\\\pm${sr[1]:d}}}'\\n\",\n    \"fmt += r'& \\\\pixmat[mm]{{{sp[0]:.2f}$\\\\pm${spr[0]:.2f}}}{{{sp[1]:.2f}$\\\\pm${spr[1]:.2f}}}{{{sp[2]:.2f}$\\\\pm${spr[1]:.2f}}}'\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"for site in sites:\\n\",\n    \"    subabide = abide.loc[abide.site.str.contains(site)]\\n\",\n    \"\\n\",\n    \"    medians = np.median(\\n\",\n    \"        subabide[['size_x', 'size_y', 'size_z', 'spacing_x', 'spacing_y', 'spacing_z']], axis=0\\n\",\n    \"    )\\n\",\n    \"\\n\",\n    \"    mins = np.abs(\\n\",\n    \"        medians\\n\",\n    \"        - np.min(\\n\",\n    \"            subabide[['size_x', 'size_y', 'size_z', 'spacing_x', 'spacing_y', 'spacing_z']], axis=0\\n\",\n    \"        )\\n\",\n    \"    )\\n\",\n    \"\\n\",\n    \"    maxs = np.abs(\\n\",\n    \"        medians\\n\",\n    \"        - np.max(\\n\",\n    \"            subabide[['size_x', 'size_y', 'size_z', 'spacing_x', 'spacing_y', 'spacing_z']], axis=0\\n\",\n    \"        )\\n\",\n    \"    )\\n\",\n    \"\\n\",\n    \"    ranges = np.max(np.vstack((maxs, mins)), axis=0)\\n\",\n    \"\\n\",\n    \"    print(\\n\",\n    \"        fmt.format(\\n\",\n    \"            site=site,\\n\",\n    \"            size=tuple(medians[:3].astype(int)),\\n\",\n    \"            sr=tuple(ranges[:3].astype(int)),\\n\",\n    \"            sp=tuple(medians[3:]),\\n\",\n    \"            spr=tuple(ranges[3:]),\\n\",\n    \"        )\\n\",\n    \"    )\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# data_path = '/home/oesteban/Google Drive/mriqc'\\n\",\n    \"data_path = '/home/oesteban/tmp/mriqc-ml-tests-2/'\\n\",\n    \"out_path = data_path\\n\",\n    \"loso = pd.read_csv(op.join(data_path, 'cv_loso_inner.csv'), index_col=False)\\n\",\n    \"kfold = pd.read_csv(op.join(data_path, 'cv_kfold_inner.csv'), index_col=False)\\n\",\n    \"\\n\",\n    \"kfold_outer = pd.read_csv(op.join(data_path, 'cv_kfold_outer.csv'), index_col=False)\\n\",\n    \"loso_outer = pd.read_csv(op.join(data_path, 'cv_loso_outer.csv'), index_col=False)\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"def gen_newparams(dataframe):\\n\",\n    \"    thisdf = dataframe.copy()\\n\",\n    \"    thisdf['zscored_str'] = ['nzs'] * len(thisdf['zscored'])\\n\",\n    \"    thisdf.loc[thisdf.zscored == 1, 'zscored_str'] = 'zs'\\n\",\n    \"    thisdf['params'] = thisdf['clf'] + '-' + thisdf['zscored_str'] + ' ' + thisdf['params']\\n\",\n    \"    del thisdf['zscored_str']\\n\",\n    \"    return thisdf\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"loso = gen_newparams(loso)\\n\",\n    \"kfold = gen_newparams(kfold)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"loso_models_list = list(set(loso.params.values.ravel().tolist()))\\n\",\n    \"kfold_models_list = list(set(kfold.params.values.ravel().tolist()))\\n\",\n    \"\\n\",\n    \"best_param = {}\\n\",\n    \"\\n\",\n    \"spstr = ['LoSo', '10-fold']\\n\",\n    \"best_models = {}\\n\",\n    \"for i, split_cv in enumerate([loso, kfold]):\\n\",\n    \"    best_models[spstr[i]] = {}\\n\",\n    \"    splitcols = [col for col in split_cv.columns.ravel() if col.startswith('split0')]\\n\",\n    \"    for clf in [\\n\",\n    \"        'svc_linear-nzs',\\n\",\n    \"        'svc_rbf-nzs',\\n\",\n    \"        'rfc-nzs',\\n\",\n    \"        'svc_linear-zs',\\n\",\n    \"        'svc_rbf-zs',\\n\",\n    \"        'rfc-zs',\\n\",\n    \"    ]:\\n\",\n    \"        thismodeldf = split_cv.loc[split_cv.params.str.contains(clf)]\\n\",\n    \"        max_auc = thismodeldf.mean_auc.max()\\n\",\n    \"        best = thismodeldf.loc[thismodeldf.mean_auc >= max_auc]\\n\",\n    \"        best_list = best.params.values.ravel().tolist()\\n\",\n    \"\\n\",\n    \"        if len(best_list) == 1:\\n\",\n    \"            best_models[spstr[i]][clf] = best_list[0]\\n\",\n    \"        else:\\n\",\n    \"            overall_means = [\\n\",\n    \"                thismodeldf.loc[thismodeldf.params.str.contains(pset), 'mean_auc'].mean()\\n\",\n    \"                for pset in best_list\\n\",\n    \"            ]\\n\",\n    \"            overall_max = np.max(overall_means)\\n\",\n    \"            if sum([val >= overall_max for val in overall_means]) == 1:\\n\",\n    \"                best_models[spstr[i]][clf] = best_list[np.argmax(overall_means)]\\n\",\n    \"            else:\\n\",\n    \"                best_models[spstr[i]][clf] = best_list[0]\\n\",\n    \"\\n\",\n    \"newdict = {'AUC': [], 'Classifier': [], 'Split scheme': []}\\n\",\n    \"\\n\",\n    \"modelnames = {\\n\",\n    \"    'rfc-nzs': 'RFC-nzs',\\n\",\n    \"    'rfc-zs': 'RFC-zs',\\n\",\n    \"    'svc_linear-nzs': 'SVC_lin-nzs',\\n\",\n    \"    'svc_linear-zs': 'SVC_lin-zs',\\n\",\n    \"    'svc_rbf-nzs': 'SVC_rbf-nzs',\\n\",\n    \"    'svc_rbf-zs': 'SVC_rbf-zs',\\n\",\n    \"}\\n\",\n    \"\\n\",\n    \"for key, val in list(best_models['LoSo'].items()):\\n\",\n    \"    scores = loso.loc[loso.params.str.contains(val), 'mean_auc'].values.ravel().tolist()\\n\",\n    \"    nscores = len(scores)\\n\",\n    \"\\n\",\n    \"    newdict['AUC'] += scores\\n\",\n    \"    newdict['Classifier'] += [modelnames[key]] * nscores\\n\",\n    \"    newdict['Split scheme'] += ['LoSo (16 folds)'] * nscores\\n\",\n    \"\\n\",\n    \"for key, val in list(best_models['10-fold'].items()):\\n\",\n    \"    scores = kfold.loc[kfold.params.str.contains(val), 'mean_auc'].values.ravel().tolist()\\n\",\n    \"    nscores = len(scores)\\n\",\n    \"\\n\",\n    \"    newdict['AUC'] += scores\\n\",\n    \"    newdict['Classifier'] += [modelnames[key]] * nscores\\n\",\n    \"    newdict['Split scheme'] += ['10-fold'] * nscores\\n\",\n    \"\\n\",\n    \"newdf = pd.DataFrame(newdict).sort_values(by=['Split scheme', 'Classifier'])\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"def plot_cv_outer(\\n\",\n    \"    data, score='auc', zscored=0, ax=None, ds030_score=None, split_type='LoSo', color='dodgerblue'\\n\",\n    \"):\\n\",\n    \"    if ax is None:\\n\",\n    \"        ax = plt.gca()\\n\",\n    \"\\n\",\n    \"    outer_score = data.loc[data[score].notnull(), [score, 'zscored']]\\n\",\n    \"    sns.distplot(\\n\",\n    \"        outer_score.loc[outer_score.zscored == zscored, score],\\n\",\n    \"        hist=True,\\n\",\n    \"        norm_hist=True,\\n\",\n    \"        ax=ax,\\n\",\n    \"        color=color,\\n\",\n    \"        label=split_type,\\n\",\n    \"    )\\n\",\n    \"    ax.set_xlim([0.4, 1.0])\\n\",\n    \"    ax.grid(False)\\n\",\n    \"    ax.set_yticklabels([])\\n\",\n    \"\\n\",\n    \"    mean = outer_score.loc[outer_score.zscored == zscored, score].mean()\\n\",\n    \"    std = outer_score.loc[outer_score.zscored == zscored, score].std()\\n\",\n    \"\\n\",\n    \"    mean_coord = draw_line(mean, ax=ax, color=color, lw=2.0, marker='o', extend=True)\\n\",\n    \"\\n\",\n    \"    ymax = ax.get_ylim()[1]\\n\",\n    \"    draw_line(mean - std, ax=ax, color=color, extend=True)\\n\",\n    \"    draw_line(mean + std, ax=ax, color=color, extend=True)\\n\",\n    \"\\n\",\n    \"    ax.annotate(\\n\",\n    \"        rf'$\\\\mu$={mean:0.3f}',\\n\",\n    \"        xy=(mean_coord[0], 0.75 * ymax),\\n\",\n    \"        xytext=(-35, 30),\\n\",\n    \"        textcoords='offset points',\\n\",\n    \"        va='center',\\n\",\n    \"        color='w',\\n\",\n    \"        size=14,\\n\",\n    \"        bbox={'boxstyle': 'round', 'fc': color, 'ec': 'none', 'color': 'none', 'lw': 0},\\n\",\n    \"        arrowprops={\\n\",\n    \"            'arrowstyle': 'wedge,tail_width=0.8',\\n\",\n    \"            'lw': 0,\\n\",\n    \"            'patchA': None,\\n\",\n    \"            'patchB': None,\\n\",\n    \"            'fc': color,\\n\",\n    \"            'ec': 'none',\\n\",\n    \"            'relpos': (0.5, 0.5),\\n\",\n    \"        },\\n\",\n    \"    )\\n\",\n    \"    sigmay = 0.70 * ymax\\n\",\n    \"    ax.annotate(\\n\",\n    \"        s='',\\n\",\n    \"        xy=(mean - std, sigmay),\\n\",\n    \"        xytext=(mean + std, sigmay),\\n\",\n    \"        arrowprops={'arrowstyle': '<->'},\\n\",\n    \"    )\\n\",\n    \"    ax.annotate(\\n\",\n    \"        r'$2\\\\sigma$=%0.3f' % (2 * std),\\n\",\n    \"        xy=(mean_coord[0], 0.70 * ymax),\\n\",\n    \"        xytext=(-25, -12),\\n\",\n    \"        textcoords='offset points',\\n\",\n    \"        va='center',\\n\",\n    \"        color='k',\\n\",\n    \"        size=12,\\n\",\n    \"        bbox={\\n\",\n    \"            'boxstyle': 'round',\\n\",\n    \"            'fc': 'w',\\n\",\n    \"            'ec': 'none',\\n\",\n    \"            'color': 'none',\\n\",\n    \"            'alpha': 0.7,\\n\",\n    \"            'lw': 0,\\n\",\n    \"        },\\n\",\n    \"    )\\n\",\n    \"\\n\",\n    \"    if ds030_score is not None:\\n\",\n    \"        ds030_coord = draw_line(ds030_score, ax=ax, color='k', marker='o')\\n\",\n    \"        ax.annotate(\\n\",\n    \"            'DS030',\\n\",\n    \"            xy=ds030_coord,\\n\",\n    \"            xytext=(-100, 0),\\n\",\n    \"            textcoords='offset points',\\n\",\n    \"            va='center',\\n\",\n    \"            color='w',\\n\",\n    \"            size=16,\\n\",\n    \"            bbox={'boxstyle': 'round', 'fc': color, 'ec': 'none', 'color': 'none', 'lw': 0},\\n\",\n    \"            arrowprops={\\n\",\n    \"                'arrowstyle': 'wedge,tail_width=0.8',\\n\",\n    \"                'lw': 0,\\n\",\n    \"                'patchA': None,\\n\",\n    \"                'patchB': None,\\n\",\n    \"                'fc': color,\\n\",\n    \"                'ec': 'none',\\n\",\n    \"                'relpos': (0.5, 0.5),\\n\",\n    \"            },\\n\",\n    \"        )\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"def draw_line(score, ax=None, color='k', marker=None, lw=0.7, extend=False):\\n\",\n    \"    if ax is None:\\n\",\n    \"        ax = plt.gca()\\n\",\n    \"\\n\",\n    \"    if score > 1.0:\\n\",\n    \"        score = 1.0\\n\",\n    \"\\n\",\n    \"    coords = [score, -1]\\n\",\n    \"    pdf_points = ax.lines[0].get_data()\\n\",\n    \"    coords[1] = np.interp([coords[0]], pdf_points[0], pdf_points[1])\\n\",\n    \"\\n\",\n    \"    if extend:\\n\",\n    \"        ax.axvline(coords[0], ymin=coords[1] / ax.get_ylim()[1], ymax=0.75, color='gray', lw=0.7)\\n\",\n    \"\\n\",\n    \"    ax.axvline(\\n\",\n    \"        coords[0],\\n\",\n    \"        ymin=coords[1] / ax.get_ylim()[1],\\n\",\n    \"        ymax=0,\\n\",\n    \"        color=color,\\n\",\n    \"        marker=marker,\\n\",\n    \"        markevery=2,\\n\",\n    \"        markeredgewidth=1.5,\\n\",\n    \"        markerfacecolor='w',\\n\",\n    \"        markeredgecolor=color,\\n\",\n    \"        lw=lw,\\n\",\n    \"    )\\n\",\n    \"\\n\",\n    \"    return coords\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"sns.set(style='whitegrid')\\n\",\n    \"\\n\",\n    \"fig = plt.figure(figsize=(20, 8))\\n\",\n    \"ax1 = plt.subplot2grid((2, 4), (0, 0), colspan=2, rowspan=2)\\n\",\n    \"\\n\",\n    \"sns.violinplot(\\n\",\n    \"    x='Classifier',\\n\",\n    \"    y='AUC',\\n\",\n    \"    hue='Split scheme',\\n\",\n    \"    data=newdf,\\n\",\n    \"    split=True,\\n\",\n    \"    palette=['dodgerblue', 'darkorange'],\\n\",\n    \"    ax=ax1,\\n\",\n    \")\\n\",\n    \"ax1.set_ylim([0.70, 1.0])\\n\",\n    \"ax1.set_ylabel('AUC')\\n\",\n    \"ax1.set_xlabel('Model')\\n\",\n    \"ax1.set_title('Model selection - Inner loop of nested cross-validation')\\n\",\n    \"\\n\",\n    \"ax2 = plt.subplot2grid((2, 4), (0, 2))\\n\",\n    \"plot_cv_outer(kfold_outer, zscored=0, score='auc', ax=ax2, ds030_score=0.695, split_type='10-fold')\\n\",\n    \"ax2.set_xlabel('')\\n\",\n    \"ax2.legend()\\n\",\n    \"ax2.set_title('Evaluation - Outer loop of nested cross-validation')\\n\",\n    \"ax2.title.set_position([1.1, 1.0])\\n\",\n    \"\\n\",\n    \"ax3 = plt.subplot2grid((2, 4), (1, 2))\\n\",\n    \"plot_cv_outer(\\n\",\n    \"    loso_outer,\\n\",\n    \"    zscored=0,\\n\",\n    \"    score='auc',\\n\",\n    \"    ax=ax3,\\n\",\n    \"    ds030_score=0.695,\\n\",\n    \"    color='darkorange',\\n\",\n    \"    split_type='LoSo (17 folds)',\\n\",\n    \")\\n\",\n    \"ax3.legend()\\n\",\n    \"ax3.set_xlabel('AUC')\\n\",\n    \"\\n\",\n    \"ax4 = plt.subplot2grid((2, 4), (0, 3))\\n\",\n    \"plot_cv_outer(\\n\",\n    \"    kfold_outer, zscored=0, score='acc', ax=ax4, ds030_score=0.7283, split_type='10-fold'\\n\",\n    \")\\n\",\n    \"ax4.set_xlabel('')\\n\",\n    \"ax4.legend()\\n\",\n    \"\\n\",\n    \"ax5 = plt.subplot2grid((2, 4), (1, 3))\\n\",\n    \"plot_cv_outer(\\n\",\n    \"    loso_outer,\\n\",\n    \"    zscored=0,\\n\",\n    \"    score='acc',\\n\",\n    \"    ax=ax5,\\n\",\n    \"    ds030_score=0.7283,\\n\",\n    \"    color='darkorange',\\n\",\n    \"    split_type='LoSo (17 folds)',\\n\",\n    \")\\n\",\n    \"ax5.legend()\\n\",\n    \"ax5.set_xlabel('Accuracy')\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"fig.savefig(op.join(out_path, 'crossvalidation.pdf'), bbox_inches='tight', pad_inches=0, dpi=300)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"zscoreddf = loso_outer.loc[loso_outer.zscored == 0, ['auc', 'acc', 'site']]\\n\",\n    \"palette = sns.color_palette('cubehelix', len(set(zscoreddf.site)))\\n\",\n    \"sns.pairplot(\\n\",\n    \"    zscoreddf.loc[zscoreddf.auc.notnull(), ['auc', 'acc', 'site']], hue='site', palette=palette\\n\",\n    \")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"sites = sorted(set(loso_outer.site.ravel().tolist()))\\n\",\n    \"palette = sns.color_palette('husl', len(sites))\\n\",\n    \"fig = plt.figure()\\n\",\n    \"for i, site in enumerate(sites):\\n\",\n    \"    sitedf = loso_outer.loc[loso_outer.site == site]\\n\",\n    \"    accdf = sitedf.loc[sitedf.zscored == 0]\\n\",\n    \"    sns.distplot(accdf.acc.values.ravel(), bins=20, kde=0, label=site, color=palette[i])\\n\",\n    \"\\n\",\n    \"fig.gca().legend()\\n\",\n    \"fig.gca().set_xlim([0.5, 1.0])\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python 3\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.6.1\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "docs/notebooks/Paper-v2.0.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Generation of tables and figures of MRIQC paper\\n\",\n    \"\\n\",\n    \"This notebook is associated to the paper:\\n\",\n    \"\\n\",\n    \"Esteban O, Birman D, Schaer M, Koyejo OO, Poldrack RA, Gorgolewski KJ; MRIQC: Predicting Quality in Manual MRI Assessment Protocols Using No-Reference Image Quality Measures; bioRxiv 111294; doi:[10.1101/111294](https://doi.org/10.1101/111294).\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true,\n    \"scrolled\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"%matplotlib inline\\n\",\n    \"%load_ext autoreload\\n\",\n    \"%autoreload 2\\n\",\n    \"import os.path as op\\n\",\n    \"\\n\",\n    \"import numpy as np\\n\",\n    \"import pandas as pd\\n\",\n    \"from mriqc.classifier.data import combine_datasets, read_dataset\\n\",\n    \"from mriqc.viz import misc as mviz\\n\",\n    \"from pkg_resources import resource_filename as pkgrf\\n\",\n    \"\\n\",\n    \"# Where the outputs should be saved\\n\",\n    \"outputs_path = '../../mriqc-data/'\\n\",\n    \"\\n\",\n    \"# Path to ABIDE's BIDS structure\\n\",\n    \"abide_path = '/home/oesteban/Data/ABIDE/'\\n\",\n    \"# Path to DS030's BIDS structure\\n\",\n    \"ds030_path = '/home/oesteban/Data/ds030/'\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Read some data (from mriqc package)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"x_path = pkgrf('mriqc', 'data/csv/x_abide.csv')\\n\",\n    \"y_path = pkgrf('mriqc', 'data/csv/y_abide.csv')\\n\",\n    \"ds030_x_path = pkgrf('mriqc', 'data/csv/x_ds030.csv')\\n\",\n    \"ds030_y_path = pkgrf('mriqc', 'data/csv/y_ds030.csv')\\n\",\n    \"\\n\",\n    \"rater_types = {'rater_1': float, 'rater_2': float, 'rater_3': float}\\n\",\n    \"mdata = pd.read_csv(y_path, index_col=False, dtype=rater_types)\\n\",\n    \"\\n\",\n    \"sites = sorted(set(mdata.site.values.ravel().tolist()))\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Figure 1: artifacts in MRI\\n\",\n    \"Shows a couple of subpar datasets from the ABIDE dataset\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"out_file = op.join(outputs_path, 'figures', 'fig01-artifacts.svg')\\n\",\n    \"mviz.figure1(\\n\",\n    \"    op.join(abide_path, 'sub-50137', 'anat', 'sub-50137_T1w.nii.gz'),\\n\",\n    \"    op.join(abide_path, 'sub-50110', 'anat', 'sub-50110_T1w.nii.gz'),\\n\",\n    \"    out_file,\\n\",\n    \")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"out_file_pdf = out_file[:4] + '.pdf'\\n\",\n    \"!rsvg-convert -f pdf -o $out_file_pdf $out_file\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Figure 2: batch effects\\n\",\n    \"\\n\",\n    \"This code was use to generate the second figure\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"from mriqc.classifier.sklearn import preprocessing as mcsp\\n\",\n    \"\\n\",\n    \"# Concatenate ABIDE & DS030\\n\",\n    \"fulldata = combine_datasets(\\n\",\n    \"    [\\n\",\n    \"        (x_path, y_path, 'ABIDE'),\\n\",\n    \"        (ds030_x_path, ds030_y_path, 'DS030'),\\n\",\n    \"    ]\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"# Names of all features\\n\",\n    \"features = [\\n\",\n    \"    'cjv',\\n\",\n    \"    'cnr',\\n\",\n    \"    'efc',\\n\",\n    \"    'fber',\\n\",\n    \"    'fwhm_avg',\\n\",\n    \"    'fwhm_x',\\n\",\n    \"    'fwhm_y',\\n\",\n    \"    'fwhm_z',\\n\",\n    \"    'icvs_csf',\\n\",\n    \"    'icvs_gm',\\n\",\n    \"    'icvs_wm',\\n\",\n    \"    'inu_med',\\n\",\n    \"    'inu_range',\\n\",\n    \"    'qi_1',\\n\",\n    \"    'qi_2',\\n\",\n    \"    'rpve_csf',\\n\",\n    \"    'rpve_gm',\\n\",\n    \"    'rpve_wm',\\n\",\n    \"    'size_x',\\n\",\n    \"    'size_y',\\n\",\n    \"    'size_z',\\n\",\n    \"    'snr_csf',\\n\",\n    \"    'snr_gm',\\n\",\n    \"    'snr_total',\\n\",\n    \"    'snr_wm',\\n\",\n    \"    'snrd_csf',\\n\",\n    \"    'snrd_gm',\\n\",\n    \"    'snrd_total',\\n\",\n    \"    'snrd_wm',\\n\",\n    \"    'spacing_x',\\n\",\n    \"    'spacing_y',\\n\",\n    \"    'spacing_z',\\n\",\n    \"    'summary_bg_k',\\n\",\n    \"    'summary_bg_mad',\\n\",\n    \"    'summary_bg_mean',\\n\",\n    \"    'summary_bg_median',\\n\",\n    \"    'summary_bg_n',\\n\",\n    \"    'summary_bg_p05',\\n\",\n    \"    'summary_bg_p95',\\n\",\n    \"    'summary_bg_stdv',\\n\",\n    \"    'summary_csf_k',\\n\",\n    \"    'summary_csf_mad',\\n\",\n    \"    'summary_csf_mean',\\n\",\n    \"    'summary_csf_median',\\n\",\n    \"    'summary_csf_n',\\n\",\n    \"    'summary_csf_p05',\\n\",\n    \"    'summary_csf_p95',\\n\",\n    \"    'summary_csf_stdv',\\n\",\n    \"    'summary_gm_k',\\n\",\n    \"    'summary_gm_mad',\\n\",\n    \"    'summary_gm_mean',\\n\",\n    \"    'summary_gm_median',\\n\",\n    \"    'summary_gm_n',\\n\",\n    \"    'summary_gm_p05',\\n\",\n    \"    'summary_gm_p95',\\n\",\n    \"    'summary_gm_stdv',\\n\",\n    \"    'summary_wm_k',\\n\",\n    \"    'summary_wm_mad',\\n\",\n    \"    'summary_wm_mean',\\n\",\n    \"    'summary_wm_median',\\n\",\n    \"    'summary_wm_n',\\n\",\n    \"    'summary_wm_p05',\\n\",\n    \"    'summary_wm_p95',\\n\",\n    \"    'summary_wm_stdv',\\n\",\n    \"    'tpm_overlap_csf',\\n\",\n    \"    'tpm_overlap_gm',\\n\",\n    \"    'tpm_overlap_wm',\\n\",\n    \"    'wm2max',\\n\",\n    \"]\\n\",\n    \"\\n\",\n    \"# Names of features that can be normalized\\n\",\n    \"coi = [\\n\",\n    \"    'cjv',\\n\",\n    \"    'cnr',\\n\",\n    \"    'efc',\\n\",\n    \"    'fber',\\n\",\n    \"    'fwhm_avg',\\n\",\n    \"    'fwhm_x',\\n\",\n    \"    'fwhm_y',\\n\",\n    \"    'fwhm_z',\\n\",\n    \"    'snr_csf',\\n\",\n    \"    'snr_gm',\\n\",\n    \"    'snr_total',\\n\",\n    \"    'snr_wm',\\n\",\n    \"    'snrd_csf',\\n\",\n    \"    'snrd_gm',\\n\",\n    \"    'snrd_total',\\n\",\n    \"    'snrd_wm',\\n\",\n    \"    'summary_csf_mad',\\n\",\n    \"    'summary_csf_mean',\\n\",\n    \"    'summary_csf_median',\\n\",\n    \"    'summary_csf_p05',\\n\",\n    \"    'summary_csf_p95',\\n\",\n    \"    'summary_csf_stdv',\\n\",\n    \"    'summary_gm_k',\\n\",\n    \"    'summary_gm_mad',\\n\",\n    \"    'summary_gm_mean',\\n\",\n    \"    'summary_gm_median',\\n\",\n    \"    'summary_gm_p05',\\n\",\n    \"    'summary_gm_p95',\\n\",\n    \"    'summary_gm_stdv',\\n\",\n    \"    'summary_wm_k',\\n\",\n    \"    'summary_wm_mad',\\n\",\n    \"    'summary_wm_mean',\\n\",\n    \"    'summary_wm_median',\\n\",\n    \"    'summary_wm_p05',\\n\",\n    \"    'summary_wm_p95',\\n\",\n    \"    'summary_wm_stdv',\\n\",\n    \"]\\n\",\n    \"\\n\",\n    \"# Plot batches\\n\",\n    \"fig = mviz.plot_batches(\\n\",\n    \"    fulldata,\\n\",\n    \"    cols=list(reversed(coi)),\\n\",\n    \"    out_file=op.join(outputs_path, 'figures/fig02-batches-a.pdf'),\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"# Apply new site-wise scaler\\n\",\n    \"scaler = mcsp.BatchRobustScaler(by='site', columns=coi)\\n\",\n    \"scaled = scaler.fit_transform(fulldata)\\n\",\n    \"fig = mviz.plot_batches(\\n\",\n    \"    scaled,\\n\",\n    \"    cols=coi,\\n\",\n    \"    site_labels='right',\\n\",\n    \"    out_file=op.join(outputs_path, 'figures/fig02-batches-b.pdf'),\\n\",\n    \")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Figure 3: Inter-rater variability\\n\",\n    \"\\n\",\n    \"In this figure we evaluate the inter-observer agreement between both raters on the 100 data points overlapping of ABIDE. Also the Cohen's Kappa is computed.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"from sklearn.metrics import cohen_kappa_score\\n\",\n    \"\\n\",\n    \"overlap = mdata[np.all(~np.isnan(mdata[['rater_1', 'rater_2']]), axis=1)]\\n\",\n    \"y1 = overlap.rater_1.values.ravel().tolist()\\n\",\n    \"y2 = overlap.rater_2.values.ravel().tolist()\\n\",\n    \"fig = mviz.inter_rater_variability(\\n\",\n    \"    y1, y2, out_file=op.join(outputs_path, 'figures', 'fig02-irv.pdf')\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"print(f\\\"Cohen's Kappa {cohen_kappa_score(y1, y2):f}\\\")\\n\",\n    \"\\n\",\n    \"y1 = overlap.rater_1.values.ravel()\\n\",\n    \"y1[y1 == 0] = 1\\n\",\n    \"\\n\",\n    \"y2 = overlap.rater_2.values.ravel()\\n\",\n    \"y2[y2 == 0] = 1\\n\",\n    \"print(f\\\"Cohen's Kappa (binarized): {cohen_kappa_score(y1, y2):f}\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Figure 5: Model selection\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import matplotlib.pyplot as plt\\n\",\n    \"import seaborn as sns\\n\",\n    \"\\n\",\n    \"rfc_acc = [\\n\",\n    \"    0.842,\\n\",\n    \"    0.815,\\n\",\n    \"    0.648,\\n\",\n    \"    0.609,\\n\",\n    \"    0.789,\\n\",\n    \"    0.761,\\n\",\n    \"    0.893,\\n\",\n    \"    0.833,\\n\",\n    \"    0.842,\\n\",\n    \"    0.767,\\n\",\n    \"    0.806,\\n\",\n    \"    0.850,\\n\",\n    \"    0.878,\\n\",\n    \"    0.798,\\n\",\n    \"    0.559,\\n\",\n    \"    0.881,\\n\",\n    \"    0.375,\\n\",\n    \"]\\n\",\n    \"svc_lin_acc = [\\n\",\n    \"    0.947,\\n\",\n    \"    0.667,\\n\",\n    \"    0.870,\\n\",\n    \"    0.734,\\n\",\n    \"    0.754,\\n\",\n    \"    0.701,\\n\",\n    \"    0.750,\\n\",\n    \"    0.639,\\n\",\n    \"    0.877,\\n\",\n    \"    0.767,\\n\",\n    \"    0.500,\\n\",\n    \"    0.475,\\n\",\n    \"    0.837,\\n\",\n    \"    0.768,\\n\",\n    \"    0.717,\\n\",\n    \"    0.050,\\n\",\n    \"    0.429,\\n\",\n    \"]\\n\",\n    \"svc_rbf_acc = [\\n\",\n    \"    0.947,\\n\",\n    \"    0.852,\\n\",\n    \"    0.500,\\n\",\n    \"    0.578,\\n\",\n    \"    0.772,\\n\",\n    \"    0.712,\\n\",\n    \"    0.821,\\n\",\n    \"    0.583,\\n\",\n    \"    0.912,\\n\",\n    \"    0.767,\\n\",\n    \"    0.500,\\n\",\n    \"    0.450,\\n\",\n    \"    0.837,\\n\",\n    \"    0.778,\\n\",\n    \"    0.441,\\n\",\n    \"    0.950,\\n\",\n    \"    0.339,\\n\",\n    \"]\\n\",\n    \"\\n\",\n    \"df = pd.DataFrame(\\n\",\n    \"    {\\n\",\n    \"        'site': list(range(len(sites))) * 3,\\n\",\n    \"        'accuracy': rfc_acc + svc_lin_acc + svc_rbf_acc,\\n\",\n    \"        'Model': ['RFC'] * len(sites) + ['SVC_lin'] * len(sites) + ['SVC_rbf'] * len(sites),\\n\",\n    \"    }\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"x = np.arange(len(sites))\\n\",\n    \"data = list(zip(rfc_acc, svc_lin_acc, svc_rbf_acc))\\n\",\n    \"\\n\",\n    \"dim = len(data[0])\\n\",\n    \"w = 0.81\\n\",\n    \"dimw = w / dim\\n\",\n    \"\\n\",\n    \"colors = ['dodgerblue', 'orange', 'darkorange']\\n\",\n    \"\\n\",\n    \"allvals = [rfc_acc, svc_lin_acc, svc_rbf_acc]\\n\",\n    \"\\n\",\n    \"fig = plt.figure(figsize=(10, 3))\\n\",\n    \"ax2 = plt.subplot2grid((1, 4), (0, 3))\\n\",\n    \"plot = sns.violinplot(\\n\",\n    \"    data=df, x='Model', y='accuracy', ax=ax2, palette=colors, bw=0.1, linewidth=0.7\\n\",\n    \")\\n\",\n    \"for i in range(dim):\\n\",\n    \"    ax2.axhline(np.average(allvals[i]), ls='--', color=colors[i], lw=0.8)\\n\",\n    \"#     ax2.axhline(np.percentile(allvals[i], 50), ls='--', color=colors[i], lw=.8)\\n\",\n    \"# sns.swarmplot(x=\\\"model\\\", y=\\\"accuracy\\\", data=df, color=\\\"w\\\", alpha=.5, ax=ax2);\\n\",\n    \"ax2.yaxis.tick_right()\\n\",\n    \"ax2.set_ylabel('')\\n\",\n    \"ax2.set_xticklabels(ax2.get_xticklabels(), rotation=40)\\n\",\n    \"ax2.set_ylim([0.0, 1.0])\\n\",\n    \"\\n\",\n    \"ax1 = plt.subplot2grid((1, 4), (0, 0), colspan=3)\\n\",\n    \"for i in range(dim):\\n\",\n    \"    y = [d[i] for d in data]\\n\",\n    \"    b = ax1.bar(x + i * dimw, y, dimw, bottom=0.001, color=colors[i], alpha=0.6)\\n\",\n    \"    print(np.average(allvals[i]), np.std(allvals[i]))\\n\",\n    \"    ax1.axhline(np.average(allvals[i]), ls='--', color=colors[i], lw=0.8)\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"plt.xlim([-0.2, 16.75])\\n\",\n    \"plt.grid(False)\\n\",\n    \"_ = plt.xticks(np.arange(0, 17) + 0.33, sites, rotation='vertical')\\n\",\n    \"ax1.set_ylim([0.0, 1.0])\\n\",\n    \"ax1.set_ylabel('Accuracy (ACC)')\\n\",\n    \"fig.savefig(op.join(outputs_path, 'figures/fig05-acc.pdf'), bbox_inches='tight', dpi=300)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"rfc_roc_auc = [\\n\",\n    \"    0.597,\\n\",\n    \"    0.380,\\n\",\n    \"    0.857,\\n\",\n    \"    0.610,\\n\",\n    \"    0.698,\\n\",\n    \"    0.692,\\n\",\n    \"    0.963,\\n\",\n    \"    0.898,\\n\",\n    \"    0.772,\\n\",\n    \"    0.596,\\n\",\n    \"    0.873,\\n\",\n    \"    0.729,\\n\",\n    \"    0.784,\\n\",\n    \"    0.860,\\n\",\n    \"    0.751,\\n\",\n    \"    0.900,\\n\",\n    \"    0.489,\\n\",\n    \"]\\n\",\n    \"svc_lin_roc_auc = [\\n\",\n    \"    0.583,\\n\",\n    \"    0.304,\\n\",\n    \"    0.943,\\n\",\n    \"    0.668,\\n\",\n    \"    0.691,\\n\",\n    \"    0.754,\\n\",\n    \"    1.000,\\n\",\n    \"    0.778,\\n\",\n    \"    0.847,\\n\",\n    \"    0.590,\\n\",\n    \"    0.857,\\n\",\n    \"    0.604,\\n\",\n    \"    0.604,\\n\",\n    \"    0.838,\\n\",\n    \"    0.447,\\n\",\n    \"    0.650,\\n\",\n    \"    0.501,\\n\",\n    \"]\\n\",\n    \"svc_rbf_roc_auc = [\\n\",\n    \"    0.681,\\n\",\n    \"    0.217,\\n\",\n    \"    0.827,\\n\",\n    \"    0.553,\\n\",\n    \"    0.738,\\n\",\n    \"    0.616,\\n\",\n    \"    0.889,\\n\",\n    \"    0.813,\\n\",\n    \"    0.845,\\n\",\n    \"    0.658,\\n\",\n    \"    0.779,\\n\",\n    \"    0.493,\\n\",\n    \"    0.726,\\n\",\n    \"    0.510,\\n\",\n    \"    0.544,\\n\",\n    \"    0.500,\\n\",\n    \"    0.447,\\n\",\n    \"]\\n\",\n    \"\\n\",\n    \"df = pd.DataFrame(\\n\",\n    \"    {\\n\",\n    \"        'site': list(range(len(sites))) * 3,\\n\",\n    \"        'auc': rfc_roc_auc + svc_lin_roc_auc + svc_rbf_roc_auc,\\n\",\n    \"        'Model': ['RFC'] * len(sites) + ['SVC_lin'] * len(sites) + ['SVC_rbf'] * len(sites),\\n\",\n    \"    }\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"x = np.arange(len(sites))\\n\",\n    \"data = list(zip(rfc_roc_auc, svc_lin_roc_auc, svc_rbf_roc_auc))\\n\",\n    \"\\n\",\n    \"dim = len(data[0])\\n\",\n    \"w = 0.81\\n\",\n    \"dimw = w / dim\\n\",\n    \"\\n\",\n    \"colors = ['dodgerblue', 'orange', 'darkorange']\\n\",\n    \"\\n\",\n    \"allvals = [rfc_roc_auc, svc_lin_roc_auc, svc_rbf_roc_auc]\\n\",\n    \"\\n\",\n    \"fig = plt.figure(figsize=(10, 3))\\n\",\n    \"ax2 = plt.subplot2grid((1, 4), (0, 3))\\n\",\n    \"plot = sns.violinplot(data=df, x='Model', y='auc', ax=ax2, palette=colors, bw=0.1, linewidth=0.7)\\n\",\n    \"for i in range(dim):\\n\",\n    \"    ax2.axhline(np.average(allvals[i]), ls='--', color=colors[i], lw=0.8)\\n\",\n    \"\\n\",\n    \"ax2.yaxis.tick_right()\\n\",\n    \"ax2.set_ylabel('')\\n\",\n    \"ax2.set_xticklabels(ax2.get_xticklabels(), rotation=40)\\n\",\n    \"ax2.set_ylim([0.0, 1.0])\\n\",\n    \"\\n\",\n    \"ax1 = plt.subplot2grid((1, 4), (0, 0), colspan=3)\\n\",\n    \"for i in range(dim):\\n\",\n    \"    y = [d[i] for d in data]\\n\",\n    \"    b = ax1.bar(x + i * dimw, y, dimw, bottom=0.001, color=colors[i], alpha=0.6)\\n\",\n    \"    print(np.average(allvals[i]), np.std(allvals[i]))\\n\",\n    \"    ax1.axhline(np.average(allvals[i]), ls='--', color=colors[i], lw=0.8)\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"plt.xlim([-0.2, 16.75])\\n\",\n    \"plt.grid(False)\\n\",\n    \"_ = plt.xticks(np.arange(0, 17) + 0.33, sites, rotation='vertical')\\n\",\n    \"ax1.set_ylim([0.0, 1.0])\\n\",\n    \"ax1.set_ylabel('Area under the curve (AUC)')\\n\",\n    \"fig.savefig(op.join(outputs_path, 'figures/fig05-auc.pdf'), bbox_inches='tight', dpi=300)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Evaluation on DS030\\n\",\n    \"\\n\",\n    \"This section deals with the results obtained on DS030.\\n\",\n    \"\\n\",\n    \"### Table 4: Confusion matrix\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"from sklearn.metrics import confusion_matrix\\n\",\n    \"\\n\",\n    \"pred_file = op.abspath(\\n\",\n    \"    op.join(\\n\",\n    \"        '..',\\n\",\n    \"        'mriqc/data/csv',\\n\",\n    \"        'mclf_run-20170724-191452_mod-rfc_ver-0.9.7-rc8_class-2_cv-loso_data-test_pred.csv',\\n\",\n    \"    )\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"pred_y = pd.read_csv(pred_file)\\n\",\n    \"true_y = pd.read_csv(ds030_y_path)\\n\",\n    \"true_y.rater_1 *= -1\\n\",\n    \"true_y.rater_1[true_y.rater_1 < 0] = 0\\n\",\n    \"print(\\n\",\n    \"    confusion_matrix(true_y.rater_1.tolist(), pred_y.pred_y.values.ravel().tolist(), labels=[0, 1])\\n\",\n    \")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Figure 6A: Feature importances\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import seaborn as sns\\n\",\n    \"from sklearn.externals.joblib import load as loadpkl\\n\",\n    \"\\n\",\n    \"sns.set_style('white')\\n\",\n    \"\\n\",\n    \"# Get the RFC\\n\",\n    \"estimator = loadpkl(\\n\",\n    \"    pkgrf(\\n\",\n    \"        'mriqc',\\n\",\n    \"        'data/mclf_run-20170724-191452_mod-rfc_ver-0.9.7-rc8_class-2_cv-loso_data-train_estimator.pklz',\\n\",\n    \"    )\\n\",\n    \")\\n\",\n    \"forest = estimator.named_steps['rfc']\\n\",\n    \"\\n\",\n    \"# Features selected in cross-validation\\n\",\n    \"features = [\\n\",\n    \"    'cjv',\\n\",\n    \"    'cnr',\\n\",\n    \"    'efc',\\n\",\n    \"    'fber',\\n\",\n    \"    'fwhm_avg',\\n\",\n    \"    'fwhm_x',\\n\",\n    \"    'fwhm_y',\\n\",\n    \"    'fwhm_z',\\n\",\n    \"    'icvs_csf',\\n\",\n    \"    'icvs_gm',\\n\",\n    \"    'icvs_wm',\\n\",\n    \"    'qi_1',\\n\",\n    \"    'qi_2',\\n\",\n    \"    'rpve_csf',\\n\",\n    \"    'rpve_gm',\\n\",\n    \"    'rpve_wm',\\n\",\n    \"    'snr_csf',\\n\",\n    \"    'snr_gm',\\n\",\n    \"    'snr_total',\\n\",\n    \"    'snr_wm',\\n\",\n    \"    'snrd_csf',\\n\",\n    \"    'snrd_gm',\\n\",\n    \"    'snrd_total',\\n\",\n    \"    'snrd_wm',\\n\",\n    \"    'summary_bg_k',\\n\",\n    \"    'summary_bg_stdv',\\n\",\n    \"    'summary_csf_k',\\n\",\n    \"    'summary_csf_mad',\\n\",\n    \"    'summary_csf_mean',\\n\",\n    \"    'summary_csf_median',\\n\",\n    \"    'summary_csf_p05',\\n\",\n    \"    'summary_csf_p95',\\n\",\n    \"    'summary_csf_stdv',\\n\",\n    \"    'summary_gm_k',\\n\",\n    \"    'summary_gm_mad',\\n\",\n    \"    'summary_gm_mean',\\n\",\n    \"    'summary_gm_median',\\n\",\n    \"    'summary_gm_p05',\\n\",\n    \"    'summary_gm_p95',\\n\",\n    \"    'summary_gm_stdv',\\n\",\n    \"    'summary_wm_k',\\n\",\n    \"    'summary_wm_mad',\\n\",\n    \"    'summary_wm_mean',\\n\",\n    \"    'summary_wm_median',\\n\",\n    \"    'summary_wm_p05',\\n\",\n    \"    'summary_wm_p95',\\n\",\n    \"    'summary_wm_stdv',\\n\",\n    \"    'tpm_overlap_csf',\\n\",\n    \"    'tpm_overlap_gm',\\n\",\n    \"    'tpm_overlap_wm',\\n\",\n    \"]\\n\",\n    \"\\n\",\n    \"nft = len(features)\\n\",\n    \"\\n\",\n    \"forest = estimator.named_steps['rfc']\\n\",\n    \"importances = np.median([tree.feature_importances_ for tree in forest.estimators_], axis=0)\\n\",\n    \"# importances = np.median(, axis=0)\\n\",\n    \"indices = np.argsort(importances)[::-1]\\n\",\n    \"\\n\",\n    \"df = {'Feature': [], 'Importance': []}\\n\",\n    \"for tree in forest.estimators_:\\n\",\n    \"    for i in indices:\\n\",\n    \"        df['Feature'] += [features[i]]\\n\",\n    \"        df['Importance'] += [tree.feature_importances_[i]]\\n\",\n    \"fig = plt.figure(figsize=(20, 6))\\n\",\n    \"# plt.title(\\\"Feature importance plot\\\")\\n\",\n    \"sns.boxplot(x='Feature', y='Importance', data=pd.DataFrame(df), linewidth=1, notch=True)\\n\",\n    \"plt.xlabel(f'Features selected ({len(features)})')\\n\",\n    \"# plt.bar(range(nft), importances[indices],\\n\",\n    \"#        color=\\\"r\\\", yerr=std[indices], align=\\\"center\\\")\\n\",\n    \"plt.xticks(range(nft))\\n\",\n    \"plt.gca().set_xticklabels([features[i] for i in indices], rotation=90)\\n\",\n    \"plt.xlim([-1, nft])\\n\",\n    \"plt.show()\\n\",\n    \"fig.savefig(\\n\",\n    \"    op.join(outputs_path, 'figures', 'fig06-exp2-fi.pdf'),\\n\",\n    \"    bbox_inches='tight',\\n\",\n    \"    pad_inches=0,\\n\",\n    \"    dpi=300,\\n\",\n    \")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Figure 6B: Misclassified images of DS030\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"fn = [\\n\",\n    \"    '10225',\\n\",\n    \"    '10235',\\n\",\n    \"    '10316',\\n\",\n    \"    '10339',\\n\",\n    \"    '10365',\\n\",\n    \"    '10376',\\n\",\n    \"    '10429',\\n\",\n    \"    '10460',\\n\",\n    \"    '10506',\\n\",\n    \"    '10527',\\n\",\n    \"    '10530',\\n\",\n    \"    '10624',\\n\",\n    \"    '10696',\\n\",\n    \"    '10891',\\n\",\n    \"    '10948',\\n\",\n    \"    '10968',\\n\",\n    \"    '10977',\\n\",\n    \"    '11050',\\n\",\n    \"    '11052',\\n\",\n    \"    '11142',\\n\",\n    \"    '11143',\\n\",\n    \"    '11149',\\n\",\n    \"    '50004',\\n\",\n    \"    '50005',\\n\",\n    \"    '50008',\\n\",\n    \"    '50010',\\n\",\n    \"    '50016',\\n\",\n    \"    '50027',\\n\",\n    \"    '50029',\\n\",\n    \"    '50033',\\n\",\n    \"    '50034',\\n\",\n    \"    '50036',\\n\",\n    \"    '50043',\\n\",\n    \"    '50047',\\n\",\n    \"    '50049',\\n\",\n    \"    '50053',\\n\",\n    \"    '50054',\\n\",\n    \"    '50055',\\n\",\n    \"    '50085',\\n\",\n    \"    '60006',\\n\",\n    \"    '60010',\\n\",\n    \"    '60012',\\n\",\n    \"    '60014',\\n\",\n    \"    '60016',\\n\",\n    \"    '60021',\\n\",\n    \"    '60046',\\n\",\n    \"    '60052',\\n\",\n    \"    '60072',\\n\",\n    \"    '60073',\\n\",\n    \"    '60084',\\n\",\n    \"    '60087',\\n\",\n    \"    '70051',\\n\",\n    \"    '70060',\\n\",\n    \"    '70072',\\n\",\n    \"]\\n\",\n    \"fp = ['10280', '10455', '10523', '11112', '50020', '50048', '50052', '50061', '50073', '60077']\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"fn_clear = [('10316', 98), ('10968', 122), ('11050', 110), ('11149', 111)]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import matplotlib.pyplot as plt\\n\",\n    \"import nibabel as nb\\n\",\n    \"from mriqc.viz.utils import plot_slice\\n\",\n    \"\\n\",\n    \"for im, z in fn_clear:\\n\",\n    \"    image_path = op.join(ds030_path, f'sub-{im}', 'anat', f'sub-{im}_T1w.nii.gz')\\n\",\n    \"    imdata = nb.load(image_path).get_data()\\n\",\n    \"\\n\",\n    \"    fig, ax = plt.subplots()\\n\",\n    \"    plot_slice(imdata[..., z], annotate=True)\\n\",\n    \"    fig.savefig(\\n\",\n    \"        op.join(outputs_path, 'figures', f'fig-06_sub-{im}_slice-{z:03}.svg'),\\n\",\n    \"        dpi=300,\\n\",\n    \"        bbox_inches='tight',\\n\",\n    \"    )\\n\",\n    \"    plt.clf()\\n\",\n    \"    plt.close()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"fp_clear = [\\n\",\n    \"    ('10455', 140),\\n\",\n    \"    ('50073', 162),\\n\",\n    \"]\\n\",\n    \"for im, z in fp_clear:\\n\",\n    \"    image_path = op.join(ds030_path, f'sub-{im}', 'anat', f'sub-{im}_T1w.nii.gz')\\n\",\n    \"    imdata = nb.load(image_path).get_data()\\n\",\n    \"\\n\",\n    \"    fig, ax = plt.subplots()\\n\",\n    \"    plot_slice(imdata[..., z], annotate=True)\\n\",\n    \"    fig.savefig(\\n\",\n    \"        op.join(outputs_path, 'figures', f'fig-06_sub-{im}_slice-{z:03}.svg'),\\n\",\n    \"        dpi=300,\\n\",\n    \"        bbox_inches='tight',\\n\",\n    \"    )\\n\",\n    \"    plt.clf()\\n\",\n    \"    plt.close()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": []\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python 3\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.6.1\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 1\n}\n"
  },
  {
    "path": "docs/notebooks/SpikesPlotter.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"%matplotlib inline\\n\",\n    \"%load_ext autoreload\\n\",\n    \"%autoreload 2\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import os.path as op\\n\",\n    \"\\n\",\n    \"wf_path = '/home/oesteban/tmp/mriqc-newcircle/work/workflow_enumerator/funcMRIQC/'\\n\",\n    \"\\n\",\n    \"in_fft = op.join(\\n\",\n    \"    wf_path,\\n\",\n    \"    'ComputeIQMs/_in_file_..home..oesteban..Data..example_artifacts_dataset..sub-ben01'\\n\",\n    \"    '..func..sub-ben01_task-unknown_bold.nii.gz/SpikesFinderFFT/sub-ben01_task-unknown_bold_zsfft.nii.gz',\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"in_file = op.join(\\n\",\n    \"    wf_path,\\n\",\n    \"    '_in_file_..home..oesteban..Data..example_artifacts_dataset..sub-ben01..func..sub-ben01_'\\n\",\n    \"    'task-unknown_bold.nii.gz/reorient_and_discard/sub-ben01_task-unknown_bold.nii.gz',\\n\",\n    \")\\n\",\n    \"in_spikes = op.join(\\n\",\n    \"    wf_path,\\n\",\n    \"    'ComputeIQMs/_in_file_..home..oesteban..Data..example_artifacts_dataset..sub-ben01..func..'\\n\",\n    \"    'sub-ben01_task-unknown_bold.nii.gz/SpikesFinderFFT/sub-ben01_task-unknown_bold_spikes.tsv',\\n\",\n    \")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"in_fft = op.join(\\n\",\n    \"    wf_path,\\n\",\n    \"    'ComputeIQMs/_in_file_..home..oesteban..Data..circle-tests..sub-ds205s09'\\n\",\n    \"    '..func..sub-ds205s09_task-view_acq-LR_run-02_bold.nii.gz/SpikesFinderFFT/sub-ds205s09_task-view_acq-LR_run-02_bold_zsfft.nii.gz',\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"in_file = op.join(\\n\",\n    \"    wf_path,\\n\",\n    \"    '_in_file_..home..oesteban..Data..circle-tests..sub-ds205s09..func..sub-ds205s09_'\\n\",\n    \"    'task-view_acq-LR_run-02_bold.nii.gz/reorient_and_discard/sub-ds205s09_task-view_acq-LR_run-02_bold.nii.gz',\\n\",\n    \")\\n\",\n    \"in_spikes = op.join(\\n\",\n    \"    wf_path,\\n\",\n    \"    'ComputeIQMs/_in_file_..home..oesteban..Data..circle-tests..sub-ds205s09'\\n\",\n    \"    '..func..sub-ds205s09_task-view_acq-LR_run-02_bold.nii.gz/SpikesFinderFFT/sub-ds205s09_task-view_acq-LR_run-02_bold_spikes.tsv',\\n\",\n    \")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"from mriqc.viz import utils as mvu\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"import numpy as np\\n\",\n    \"\\n\",\n    \"spikes_list = [tuple(i) for i in np.atleast_2d(np.loadtxt(in_spikes, dtype=int)).tolist()]\\n\",\n    \"spikes_list += [(5, 14)]\\n\",\n    \"mvu.plot_spikes(in_file, in_fft, spikes_list)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"%matplotlib inline\\n\",\n    \"%load_ext autoreload\\n\",\n    \"%autoreload 2\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import os.path as op\\n\",\n    \"\\n\",\n    \"data_path = '/home/oesteban/Data/ABIDE/sub-50465/anat/sub-50465_T1w.nii.gz'\\n\",\n    \"data_path = '/home/oesteban/tmp/mriqc-newcircle/work/workflow_enumerator/funcMRIQC/_in_file_..home..oesteban..Data..example_artifacts_dataset..sub-ben04..func..sub-ben04_task-unknown_bold.nii.gz/compute_tsnr/stdev.nii.gz'\\n\",\n    \"# data_path = '/home/oesteban/Data/rewardBeastBIDS2/sub-119/func/sub-119_task-rest_sbref.nii.gz'\\n\",\n    \"# data_path ='/home/oesteban/tmp/mriqc-newcircle/work/workflow_enumerator/funcMRIQC/_in_file_..home..oesteban..Data..circle-tests..sub-ds003s03..func..sub-ds003s03_task-rhymejudgment_bold.nii.gz/compute_tsnr/stdev.nii.gz'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"from mriqc.viz import utils as mvu\\n\",\n    \"\\n\",\n    \"mvu.plot_mosaic(data_path, cmap='viridis')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": []\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python 3\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.5.2\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 1\n}\n"
  },
  {
    "path": "docs/notebooks/Supplemental Materials.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Generation of supplemental materials for the MRIQC paper\\n\",\n    \"\\n\",\n    \"This notebook is associated to the paper:\\n\",\n    \"\\n\",\n    \"Esteban O, Birman D, Schaer M, Koyejo OO, Poldrack RA, Gorgolewski KJ; MRIQC: Predicting Quality in Manual MRI Assessment Protocols Using No-Reference Image Quality Measures; bioRxiv 111294; doi:[10.1101/111294](https://doi.org/10.1101/111294).\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"%matplotlib inline\\n\",\n    \"%load_ext autoreload\\n\",\n    \"%autoreload 2\\n\",\n    \"import os.path as op\\n\",\n    \"\\n\",\n    \"import numpy as np\\n\",\n    \"import pandas as pd\\n\",\n    \"from mriqc.viz import misc as mviz\\n\",\n    \"from pkg_resources import resource_filename as pkgrf\\n\",\n    \"\\n\",\n    \"outputs_path = '../../mriqc-data/'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"x_path = pkgrf('mriqc', 'data/csv/x_abide.csv')\\n\",\n    \"y_path = pkgrf('mriqc', 'data/csv/y_abide.csv')\\n\",\n    \"ds030_x_path = pkgrf('mriqc', 'data/csv/x_ds030.csv')\\n\",\n    \"ds030_y_path = pkgrf('mriqc', 'data/csv/y_ds030.csv')\\n\",\n    \"\\n\",\n    \"rater_types = {'rater_1': float, 'rater_2': float, 'rater_3': float}\\n\",\n    \"mdata = pd.read_csv(y_path, index_col=False, dtype=rater_types)\\n\",\n    \"\\n\",\n    \"sites = sorted(set(mdata.site.values.ravel().tolist()))\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"fig = mviz.raters_variability_plot(\\n\",\n    \"    mdata,\\n\",\n    \"    raters=['rater_1', 'rater_2', 'rater_3'],\\n\",\n    \"    rater_names=['Rater 1', 'Rater 2A', 'Rater 2B'],\\n\",\n    \"    out_file=op.join(outputs_path, 'figures', 'suppl-fig02.pdf'),\\n\",\n    \"    only_overlap=False,\\n\",\n    \")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"from sklearn.metrics import cohen_kappa_score\\n\",\n    \"\\n\",\n    \"overlap = mdata[np.all(~np.isnan(mdata[['rater_2', 'rater_3']]), axis=1)]\\n\",\n    \"y1 = overlap.rater_2.values.ravel().tolist()\\n\",\n    \"y2 = overlap.rater_3.values.ravel().tolist()\\n\",\n    \"\\n\",\n    \"fig = mviz.inter_rater_variability(\\n\",\n    \"    y1,\\n\",\n    \"    y2,\\n\",\n    \"    raters=['Protocol A', 'Protocol B'],\\n\",\n    \"    out_file=op.join(outputs_path, 'figures', 'suppl-intrarv.pdf'),\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"print(f\\\"Cohen's Kappa {cohen_kappa_score(y1, y2):f}\\\")\\n\",\n    \"\\n\",\n    \"y1 = overlap.rater_2.values.ravel()\\n\",\n    \"y1[y1 == 0] = 1\\n\",\n    \"\\n\",\n    \"y2 = overlap.rater_3.values.ravel()\\n\",\n    \"y2[y2 == 0] = 1\\n\",\n    \"print(f\\\"Cohen's Kappa (binarized): {cohen_kappa_score(y1, y2):f}\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"fig = mviz.plot_corrmat(x_path)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"fig = mviz.plot_histograms(x_path, y_path)\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python 3\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.6.1\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "docs/notebooks/finding_spikes.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import nibabel\\n\",\n    \"import nilearn\\n\",\n    \"import nipy\\n\",\n    \"import numpy as np\\n\",\n    \"import pylab as plt\\n\",\n    \"import seaborn as sns\\n\",\n    \"from nilearn.image import mean_img, new_img_like\\n\",\n    \"from nilearn.masking import compute_epi_mask\\n\",\n    \"from nilearn.plotting import plot_anat, plot_epi, plot_roi\\n\",\n    \"from nipy.labs.mask import compute_mask\\n\",\n    \"\\n\",\n    \"%matplotlib inline\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"compute_mask?\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"in_file = 'data/sub-ben01_task-unknown_bold.nii.gz'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"in_4d_nii = nibabel.load(in_file)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"mean_nii = mean_img(in_4d_nii)\\n\",\n    \"plot_anat(mean_nii)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# mask_nii = compute_epi_mask(in_4d_nii)\\n\",\n    \"\\n\",\n    \"mask_data = compute_mask(mean_nii.get_data())\\n\",\n    \"mask_nii = new_img_like(mean_nii, mask_data)\\n\",\n    \"\\n\",\n    \"plot_roi(mask_nii, mean_nii)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"a = np.where(mask_nii.get_data() != 0)\\n\",\n    \"bbox = np.max(a[0]) - np.min(a[0]), np.max(a[1]) - np.min(a[1]), np.max(a[2]) - np.min(a[2])\\n\",\n    \"print(bbox)\\n\",\n    \"print(np.argmax(bbox))\\n\",\n    \"longest_axis = np.argmax(bbox)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"from scipy import ndimage\\n\",\n    \"\\n\",\n    \"# Input here is a binarized and intersected mask data from previous section\\n\",\n    \"dil_mask = ndimage.binary_dilation(\\n\",\n    \"    mask_nii.get_data(), iterations=int(mask_nii.shape[longest_axis] / 8)\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"# Now, we visualize the same using `plot_roi` with data being converted to Nifti\\n\",\n    \"# image. In all new image like, reference image is the same but second argument\\n\",\n    \"# varies with data specific\\n\",\n    \"dil_mask_nii = new_img_like(mask_nii, dil_mask.astype(np.int))\\n\",\n    \"plot_roi(dil_mask_nii, mean_nii)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"dil_mask.shape\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"rep = list(mask_nii.shape)\\n\",\n    \"rep[longest_axis] = -1\\n\",\n    \"new_mask_2d = dil_mask.max(axis=longest_axis).reshape(rep)\\n\",\n    \"new_mask_2d.shape\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"rep = [1, 1, 1]\\n\",\n    \"rep[longest_axis] = mask_nii.shape[longest_axis]\\n\",\n    \"new_mask_3d = np.logical_not(np.tile(new_mask_2d, rep))\\n\",\n    \"# new_mask_3d =\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"new_mask_nii = new_img_like(mask_nii, new_mask_3d.astype(np.int))\\n\",\n    \"plot_roi(new_mask_nii, mean_nii)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"from scipy.stats.mstats import zscore\\n\",\n    \"\\n\",\n    \"data4d = in_4d_nii.get_data()\\n\",\n    \"for slice_i in range(in_4d_nii.shape[2]):\\n\",\n    \"    slice_data = data4d[:, :, slice_i, :][new_mask_3d[:, :, slice_i]].mean(axis=0)\\n\",\n    \"    slice_data = zscore(slice_data)\\n\",\n    \"    plt.plot(slice_data)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"data4d[:, :, slice_i, :][new_mask_3d[:, :, slice_i]].shape\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"data4d.shape\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"compute_mask?\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"def plot_spikes(in_file, skip=0):\\n\",\n    \"    in_4d_nii = nibabel.load(in_file)\\n\",\n    \"    mean_nii = mean_img(in_4d_nii)\\n\",\n    \"    plot_anat(mean_nii)\\n\",\n    \"    mask_data = compute_mask(mean_nii.get_data())\\n\",\n    \"    mask_nii = new_img_like(mean_nii, mask_data)\\n\",\n    \"\\n\",\n    \"    plot_roi(mask_nii, mean_nii)\\n\",\n    \"\\n\",\n    \"    a = np.where(mask_nii.get_data() != 0)\\n\",\n    \"    bbox = np.max(a[0]) - np.min(a[0]), np.max(a[1]) - np.min(a[1]), np.max(a[2]) - np.min(a[2])\\n\",\n    \"    print(bbox)\\n\",\n    \"    print(np.argmax(bbox))\\n\",\n    \"    longest_axis = np.argmax(bbox)\\n\",\n    \"\\n\",\n    \"    from scipy import ndimage\\n\",\n    \"\\n\",\n    \"    # Input here is a binarized and intersected mask data from previous section\\n\",\n    \"    dil_mask = ndimage.binary_dilation(\\n\",\n    \"        mask_nii.get_data(), iterations=int(mask_nii.shape[longest_axis] / 8)\\n\",\n    \"    )\\n\",\n    \"\\n\",\n    \"    # Now, we visualize the same using `plot_roi` with data being converted to Nifti\\n\",\n    \"    # image. In all new image like, reference image is the same but second argument\\n\",\n    \"    # varies with data specific\\n\",\n    \"    dil_mask_nii = new_img_like(mask_nii, dil_mask.astype(np.int))\\n\",\n    \"    plot_roi(dil_mask_nii, mean_nii)\\n\",\n    \"\\n\",\n    \"    rep = list(mask_nii.shape)\\n\",\n    \"    rep[longest_axis] = -1\\n\",\n    \"    new_mask_2d = dil_mask.max(axis=longest_axis).reshape(rep)\\n\",\n    \"    new_mask_2d.shape\\n\",\n    \"\\n\",\n    \"    rep = [1, 1, 1]\\n\",\n    \"    rep[longest_axis] = mask_nii.shape[longest_axis]\\n\",\n    \"    new_mask_3d = np.logical_not(np.tile(new_mask_2d, rep))\\n\",\n    \"\\n\",\n    \"    new_mask_nii = new_img_like(mask_nii, new_mask_3d.astype(np.int))\\n\",\n    \"    plot_roi(new_mask_nii, mean_nii)\\n\",\n    \"\\n\",\n    \"    from scipy.stats.mstats import zscore\\n\",\n    \"\\n\",\n    \"    data4d = in_4d_nii.get_data()[:, :, :, skip:]\\n\",\n    \"    plt.figure()\\n\",\n    \"    for slice_i in range(in_4d_nii.shape[2]):\\n\",\n    \"        slice_data = data4d[:, :, slice_i, :][new_mask_3d[:, :, slice_i]].mean(axis=0)\\n\",\n    \"        # slice_data = zscore(slice_data)\\n\",\n    \"        plt.plot(slice_data)\\n\",\n    \"    plt.title(in_file)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"plot_spikes('D:/example_artifacts_dataset/sub-ben01/func/sub-ben01_task-unknown_bold.nii.gz')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"from glob import glob\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false,\n    \"scrolled\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"for file in glob('D:/*/sub-*/func/sub-*_task-*_bold.nii.gz'):\\n\",\n    \"    plot_spikes(file)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"plot_anat?\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"glob('D:/*/sub-*/func/sub-*_task-*_bold.nii.gz')\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"anaconda-cloud\": {},\n  \"kernelspec\": {\n   \"display_name\": \"Python 3\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.5.2\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 1\n}\n"
  },
  {
    "path": "docs/source/_static/bold-1subject-1task.html",
    "content": "<!DOCTYPE html>\n    <head>\n        <style>\n            #content{\n                width:99%;\n                height:100%;\n                position:absolute;\n            }\n\n            .node{\n                background-color:#7070FF;\n                border-radius: 5px;\n                position:absolute;\n                width:20px;\n                white-space:pre-wrap;\n            }\n\n            .line{\n                position: absolute;\n                color: #C2C2C2;\n                opacity: 0.5;\n                margin: 0px;\n            }\n\n            .time{\n                position: absolute;\n                font-size: 16px;\n                color: #666666;\n                margin: 0px;\n            }\n\n            .bar{\n                position: absolute;\n                height: 1px;\n                opacity: 0.7;\n            }\n\n            .dot{\n                position: absolute;\n                width: 1px;\n                height: 1px;\n                background-color: red;\n            }\n            .label {\n                width:20px;\n                height:20px;\n                opacity: 0.7;\n                display: inline-block;\n            }\n        </style>\n    </head>\n\n    <body>\n        <div id=\"content\">\n            <div style=\"display:inline-block;\">\n    <p>Start: 2017-03-24 07:49:23</p><p>Finish: 2017-03-24 08:31:04</p><p>Duration: 41.68 minutes</p><p>Nodes: 33</p><p>Cores: 10</p>\n    </div>\n    <div style=\"display:inline-block;margin-left:60px;vertical-align: top;\">\n        <p><span><div class=\"label\" style=\"background-color:#90BBD7;\"></div> Estimated Resource</span></p>\n        <p><span><div class=\"label\" style=\"background-color:#03969D;\"></div> Actual Resource</span></p>\n        <p><span><div class=\"label\" style=\"background-color:#f00;\"></div> Failed Node</span></p>\n    </div>\n    <hr class='line' width='98%' style='top:220px;'><p class='time' style='top:200px;'> 07:49 </p><hr class='line' width='98%' style='top:720px;'><p class='time' style='top:700px;'> 07:59 </p><hr class='line' width='98%' style='top:1220px;'><p class='time' style='top:1200px;'> 08:09 </p><hr class='line' width='98%' style='top:1720px;'><p class='time' style='top:1700px;'> 08:19 </p><hr class='line' width='98%' style='top:2220px;'><p class='time' style='top:2200px;'> 08:29 </p><hr class='line' width='98%' style='top:2720px;'><p class='time' style='top:2700px;'> 08:39 </p><div class='node' style='left:60px;top:220.0px;height:3px;background-color:#7070FF;'title='metadata\nduration:0.0173234833333\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:24'></div><div class='node' style='left:90px;top:219.975085px;height:21.2529608333px;background-color:#2D2D66;'title='reorient_and_discard\nduration:0.465059216667\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:51'></div><div class='node' style='left:60px;top:243.3248375px;height:3px;background-color:#4E4EB2;'title='get_mean_RPI\nduration:0.0936077\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:57'></div><div class='node' style='left:90px;top:243.282929167px;height:4.57046083333px;background-color:#7070FF;'title='SpikesMask\nduration:0.131409216667\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:59'></div><div class='node' style='left:60px;top:249.866673333px;height:3.1278375px;background-color:#7070FF;'title='SpikesFinderBgMask\nduration:0.10255675\nstart:2017-03-24 07:49:59\nend:2017-03-24 07:50:05'></div><div class='node' style='left:120px;top:243.241736667px;height:117.068253333px;background-color:#4E4EB2;'title='SpikesFinderFFT\nduration:2.38136506667\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:52:14'></div><div class='node' style='left:60px;top:362.325140833px;height:3px;background-color:#4E4EB2;'title='PlotSpikes\nduration:0.00857918333333\nstart:2017-03-24 07:52:14\nend:2017-03-24 07:52:15'></div><div class='node' style='left:150px;top:248.020089167px;height:914.694009167px;background-color:#9B9BFF;'title='motion_correct\nduration:18.3338801833\nstart:2017-03-24 07:49:57\nend:2017-03-24 08:08:17'></div><div class='node' style='left:60px;top:1164.72765083px;height:3px;background-color:#4E4EB2;'title='get_mean_motion\nduration:0.0810094333333\nstart:2017-03-24 08:08:17\nend:2017-03-24 08:08:22'></div><div class='node' style='left:60px;top:1168.79144833px;height:912.559840833px;background-color:#4E4EB2;'title='motion_correct_A\nduration:18.2911968167\nstart:2017-03-24 08:08:22\nend:2017-03-24 08:26:39'></div><div class='node' style='left:60px;top:2083.5031px;height:3px;background-color:#4E4EB2;'title='ComputeFD\nduration:0.00843621666667\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:40'></div><div class='node' style='left:90px;top:2083.45727917px;height:3px;background-color:#9B9BFF;'title='mean\nduration:0.0805924833333\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:44'></div><div class='node' style='left:60px;top:2083.55432583px;height:3px;background-color:#2D2D66;'title='compute_tsnr\nduration:0.0925172333333\nstart:2017-03-24 08:26:40\nend:2017-03-24 08:26:45'></div><div class='node' style='left:120px;top:2083.40936417px;height:5.16629166667px;background-color:#9B9BFF;'title='quality\nduration:0.143325833333\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:48'></div><div class='node' style='left:150px;top:2083.36575417px;height:7.55766583333px;background-color:#7070FF;'title='afni_msk\nduration:0.191153316667\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:51'></div><div class='node' style='left:90px;top:2087.55112917px;height:4.216835px;background-color:#4E4EB2;'title='SharpenEPI\nduration:0.1243367\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:52'></div><div class='node' style='left:60px;top:2093.0997625px;height:3px;background-color:#4E4EB2;'title='smoothness\nduration:0.01837085\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:52'></div><div class='node' style='left:60px;top:2093.78202917px;height:3px;background-color:#4E4EB2;'title='EPIApplyMask\nduration:0.01403945\nstart:2017-03-24 08:26:52\nend:2017-03-24 08:26:53'></div><div class='node' style='left:120px;top:2093.17823917px;height:3px;background-color:#9B9BFF;'title='outliers\nduration:0.0917270333333\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:57'></div><div class='node' style='left:150px;top:2092.93884167px;height:4.41815333333px;background-color:#9B9BFF;'title='PlotBrainmask\nduration:0.128363066667\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:59'></div><div class='node' style='left:180px;top:2093.25659667px;height:47.4074008333px;background-color:#2D2D66;'title='ComputeDVARS\nduration:0.988148016667\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:27:50'></div><div class='node' style='left:60px;top:2142.6778975px;height:5.69220416667px;background-color:#4E4EB2;'title='measures\nduration:0.153844083333\nstart:2017-03-24 08:27:51\nend:2017-03-24 08:28:00'></div><div class='node' style='left:60px;top:2150.38397917px;height:3px;background-color:#9B9BFF;'title='datasink\nduration:0.0152354666667\nstart:2017-03-24 08:28:00\nend:2017-03-24 08:28:01'></div><div class='node' style='left:210px;top:2093.0195875px;height:64.497115px;background-color:#2D2D66;'title='PlotMosaicZoomed\nduration:1.3299423\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:28:11'></div><div class='node' style='left:240px;top:2087.50158083px;height:74.5807691667px;background-color:#2D2D66;'title='PlotMosaicMean\nduration:1.53161538333\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:28:16'></div><div class='node' style='left:270px;top:2087.59555917px;height:77.0407516667px;background-color:#2D2D66;'title='PlotMosaicNoise\nduration:1.58081503333\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:28:19'></div><div class='node' style='left:300px;top:2088.19381583px;height:76.5711216667px;background-color:#2D2D66;'title='PlotMosaicSD\nduration:1.57142243333\nstart:2017-03-24 08:26:45\nend:2017-03-24 08:28:19'></div><div class='node' style='left:90px;top:2094.49783167px;height:190.87407px;background-color:#2D2D66;'title='EPI2MNI\nduration:3.8574814\nstart:2017-03-24 08:26:53\nend:2017-03-24 08:30:44'></div><div class='node' style='left:60px;top:2287.38539667px;height:3px;background-color:#2D2D66;'title='ResampleSegmentation\nduration:0.0425273666667\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:47'></div><div class='node' style='left:60px;top:2289.526535px;height:11.0708483333px;background-color:#4E4EB2;'title='BigPlot\nduration:0.261416966667\nstart:2017-03-24 08:30:47\nend:2017-03-24 08:31:02'></div><div class='node' style='left:60px;top:2302.61099333px;height:3px;background-color:#7070FF;'title='MergePlots\nduration:0.0109002\nstart:2017-03-24 08:31:02\nend:2017-03-24 08:31:03'></div><div class='node' style='left:60px;top:2303.16935083px;height:3px;background-color:#4E4EB2;'title='GenerateReport\nduration:0.0138323666667\nstart:2017-03-24 08:31:03\nend:2017-03-24 08:31:04'></div><div class='node' style='left:60px;top:2303.87598083px;height:3px;background-color:#2D2D66;'title='dsplots\nduration:0.00665875\nstart:2017-03-24 08:31:04\nend:2017-03-24 08:31:04'></div><p class='time' style='top:198px;left:960px;'>Memory</p><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:219.975px;'title='1.000 GB\nduration:0.000\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:220.000px;'title='2.000 GB\nduration:0.017\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:24'></div><div class='bar' style='background-color:#90BBD7;height:20.362px;width:20.000px;left:960px; top:220.866px;'title='1.000 GB\nduration:0.447\nstart:2017-03-24 07:49:24\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:243.228px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:243.242px;'title='1.000 GB\nduration:0.001\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:243.283px;'title='2.000 GB\nduration:0.001\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:243.325px;'title='3.000 GB\nduration:0.094\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:248.005px;'title='2.000 GB\nduration:0.000\nstart:2017-03-24 07:49:57\nend:2017-03-24 07:49:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:248.020px;'title='3.000 GB\nduration:0.037\nstart:2017-03-24 07:49:57\nend:2017-03-24 07:49:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:249.853px;'title='2.000 GB\nduration:0.000\nstart:2017-03-24 07:49:59\nend:2017-03-24 07:49:59'></div><div class='bar' style='background-color:#90BBD7;height:3.128px;width:60.000px;left:960px; top:249.867px;'title='3.000 GB\nduration:0.103\nstart:2017-03-24 07:49:59\nend:2017-03-24 07:50:05'></div><div class='bar' style='background-color:#90BBD7;height:105.315px;width:40.000px;left:960px; top:254.995px;'title='2.000 GB\nduration:2.146\nstart:2017-03-24 07:50:05\nend:2017-03-24 07:52:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:362.310px;'title='1.000 GB\nduration:0.000\nstart:2017-03-24 07:52:14\nend:2017-03-24 07:52:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:362.325px;'title='2.000 GB\nduration:0.009\nstart:2017-03-24 07:52:14\nend:2017-03-24 07:52:15'></div><div class='bar' style='background-color:#90BBD7;height:799.960px;width:20.000px;left:960px; top:362.754px;'title='1.000 GB\nduration:16.039\nstart:2017-03-24 07:52:15\nend:2017-03-24 08:08:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:1164.714px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:08:17\nend:2017-03-24 08:08:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:1164.728px;'title='1.000 GB\nduration:0.081\nstart:2017-03-24 08:08:17\nend:2017-03-24 08:08:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:1168.778px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:08:22\nend:2017-03-24 08:08:22'></div><div class='bar' style='background-color:#90BBD7;height:912.560px;width:20.000px;left:960px; top:1168.791px;'title='1.000 GB\nduration:18.291\nstart:2017-03-24 08:08:22\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2083.351px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2083.366px;'title='1.000 GB\nduration:0.001\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:2083.409px;'title='2.000 GB\nduration:0.001\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:2083.457px;'title='3.000 GB\nduration:0.001\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2083.503px;'title='4.000 GB\nduration:0.001\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2083.554px;'title='5.000 GB\nduration:0.007\nstart:2017-03-24 08:26:40\nend:2017-03-24 08:26:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2083.925px;'title='4.000 GB\nduration:0.071\nstart:2017-03-24 08:26:40\nend:2017-03-24 08:26:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:2087.487px;'title='3.000 GB\nduration:0.000\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2087.502px;'title='4.000 GB\nduration:0.001\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2087.551px;'title='5.000 GB\nduration:0.001\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2087.596px;'title='6.000 GB\nduration:0.012\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2088.180px;'title='5.000 GB\nduration:0.000\nstart:2017-03-24 08:26:45\nend:2017-03-24 08:26:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2088.194px;'title='6.000 GB\nduration:0.048\nstart:2017-03-24 08:26:45\nend:2017-03-24 08:26:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2090.576px;'title='5.000 GB\nduration:0.047\nstart:2017-03-24 08:26:48\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2092.923px;'title='4.000 GB\nduration:0.000\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2092.939px;'title='5.000 GB\nduration:0.002\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2093.020px;'title='6.000 GB\nduration:0.002\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2093.100px;'title='7.000 GB\nduration:0.002\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2093.178px;'title='8.000 GB\nduration:0.002\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2093.257px;'title='9.000 GB\nduration:0.010\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2093.768px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 08:26:52\nend:2017-03-24 08:26:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2093.782px;'title='9.000 GB\nduration:0.005\nstart:2017-03-24 08:26:52\nend:2017-03-24 08:26:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2094.018px;'title='8.000 GB\nduration:0.009\nstart:2017-03-24 08:26:52\nend:2017-03-24 08:26:53'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2094.484px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:26:53\nend:2017-03-24 08:26:53'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2094.498px;'title='8.000 GB\nduration:0.065\nstart:2017-03-24 08:26:53\nend:2017-03-24 08:26:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2097.765px;'title='7.000 GB\nduration:0.032\nstart:2017-03-24 08:26:57\nend:2017-03-24 08:26:59'></div><div class='bar' style='background-color:#90BBD7;height:41.307px;width:120.000px;left:960px; top:2099.357px;'title='6.000 GB\nduration:0.866\nstart:2017-03-24 08:26:59\nend:2017-03-24 08:27:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2142.664px;'title='5.000 GB\nduration:0.000\nstart:2017-03-24 08:27:50\nend:2017-03-24 08:27:51'></div><div class='bar' style='background-color:#90BBD7;height:5.692px;width:120.000px;left:960px; top:2142.678px;'title='6.000 GB\nduration:0.154\nstart:2017-03-24 08:27:51\nend:2017-03-24 08:28:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2150.370px;'title='5.000 GB\nduration:0.000\nstart:2017-03-24 08:28:00\nend:2017-03-24 08:28:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2150.384px;'title='6.000 GB\nduration:0.015\nstart:2017-03-24 08:28:00\nend:2017-03-24 08:28:01'></div><div class='bar' style='background-color:#90BBD7;height:6.371px;width:100.000px;left:960px; top:2151.146px;'title='5.000 GB\nduration:0.167\nstart:2017-03-24 08:28:01\nend:2017-03-24 08:28:11'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2159.517px;'title='4.000 GB\nduration:0.091\nstart:2017-03-24 08:28:11\nend:2017-03-24 08:28:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:2164.082px;'title='3.000 GB\nduration:0.051\nstart:2017-03-24 08:28:16\nend:2017-03-24 08:28:19'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:2166.636px;'title='2.000 GB\nduration:0.003\nstart:2017-03-24 08:28:19\nend:2017-03-24 08:28:19'></div><div class='bar' style='background-color:#90BBD7;height:118.607px;width:20.000px;left:960px; top:2166.765px;'title='1.000 GB\nduration:2.412\nstart:2017-03-24 08:28:19\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2287.372px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2287.385px;'title='1.000 GB\nduration:0.043\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:47'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2289.512px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:30:47\nend:2017-03-24 08:30:47'></div><div class='bar' style='background-color:#90BBD7;height:11.071px;width:20.000px;left:960px; top:2289.527px;'title='1.000 GB\nduration:0.261\nstart:2017-03-24 08:30:47\nend:2017-03-24 08:31:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2302.597px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:31:02\nend:2017-03-24 08:31:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2302.611px;'title='1.000 GB\nduration:0.011\nstart:2017-03-24 08:31:02\nend:2017-03-24 08:31:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2303.156px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:31:03\nend:2017-03-24 08:31:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2303.169px;'title='1.000 GB\nduration:0.014\nstart:2017-03-24 08:31:03\nend:2017-03-24 08:31:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2303.861px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:31:04\nend:2017-03-24 08:31:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2303.876px;'title='1.000 GB\nduration:0.007\nstart:2017-03-24 08:31:04\nend:2017-03-24 08:31:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2304.209px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:31:04\nend:2017-03-24 08:31:04'></div><p class='time' style='top:198px;left:960px;'>Memory</p><div class='bar' style='background-color:#03969D;height:2082.234px;width:0.000px;left:960px; top:219.975px;'title='0.000 GB\nduration:41.685\nstart:2017-03-24 07:49:23\nend:2017-03-24 08:31:04'></div><p class='time' style='top:198px;left:420px;'>Threads</p><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:219.975px;'title='1 threads\nduration:0.000\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:220.000px;'title='2 threads\nduration:0.017\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:24'></div><div class='bar' style='background-color:#90BBD7;height:20.362px;width:20.000px;left:420px; top:220.866px;'title='1 threads\nduration:0.447\nstart:2017-03-24 07:49:24\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:243.228px;'title='0 threads\nduration:0.000\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:243.242px;'title='1 threads\nduration:0.001\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:243.283px;'title='2 threads\nduration:0.001\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:243.325px;'title='3 threads\nduration:0.094\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:248.005px;'title='2 threads\nduration:0.000\nstart:2017-03-24 07:49:57\nend:2017-03-24 07:49:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:248.020px;'title='3 threads\nduration:0.037\nstart:2017-03-24 07:49:57\nend:2017-03-24 07:49:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:249.853px;'title='2 threads\nduration:0.000\nstart:2017-03-24 07:49:59\nend:2017-03-24 07:49:59'></div><div class='bar' style='background-color:#90BBD7;height:3.128px;width:60.000px;left:420px; top:249.867px;'title='3 threads\nduration:0.103\nstart:2017-03-24 07:49:59\nend:2017-03-24 07:50:05'></div><div class='bar' style='background-color:#90BBD7;height:105.315px;width:40.000px;left:420px; top:254.995px;'title='2 threads\nduration:2.146\nstart:2017-03-24 07:50:05\nend:2017-03-24 07:52:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:362.310px;'title='1 threads\nduration:0.000\nstart:2017-03-24 07:52:14\nend:2017-03-24 07:52:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:362.325px;'title='2 threads\nduration:0.009\nstart:2017-03-24 07:52:14\nend:2017-03-24 07:52:15'></div><div class='bar' style='background-color:#90BBD7;height:799.960px;width:20.000px;left:420px; top:362.754px;'title='1 threads\nduration:16.039\nstart:2017-03-24 07:52:15\nend:2017-03-24 08:08:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:1164.714px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:08:17\nend:2017-03-24 08:08:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:1164.728px;'title='1 threads\nduration:0.081\nstart:2017-03-24 08:08:17\nend:2017-03-24 08:08:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:1168.778px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:08:22\nend:2017-03-24 08:08:22'></div><div class='bar' style='background-color:#90BBD7;height:912.560px;width:20.000px;left:420px; top:1168.791px;'title='1 threads\nduration:18.291\nstart:2017-03-24 08:08:22\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2083.351px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2083.366px;'title='1 threads\nduration:0.001\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:2083.409px;'title='2 threads\nduration:0.001\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:2083.457px;'title='3 threads\nduration:0.001\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2083.503px;'title='4 threads\nduration:0.001\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2083.554px;'title='5 threads\nduration:0.007\nstart:2017-03-24 08:26:40\nend:2017-03-24 08:26:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2083.925px;'title='4 threads\nduration:0.071\nstart:2017-03-24 08:26:40\nend:2017-03-24 08:26:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:2087.487px;'title='3 threads\nduration:0.000\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2087.502px;'title='4 threads\nduration:0.001\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2087.551px;'title='5 threads\nduration:0.001\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2087.596px;'title='6 threads\nduration:0.012\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2088.180px;'title='5 threads\nduration:0.000\nstart:2017-03-24 08:26:45\nend:2017-03-24 08:26:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2088.194px;'title='6 threads\nduration:0.048\nstart:2017-03-24 08:26:45\nend:2017-03-24 08:26:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2090.576px;'title='5 threads\nduration:0.047\nstart:2017-03-24 08:26:48\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2092.923px;'title='4 threads\nduration:0.000\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2092.939px;'title='5 threads\nduration:0.002\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2093.020px;'title='6 threads\nduration:0.002\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2093.100px;'title='7 threads\nduration:0.002\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2093.178px;'title='8 threads\nduration:0.002\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2093.257px;'title='9 threads\nduration:0.010\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2093.768px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:26:52\nend:2017-03-24 08:26:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2093.782px;'title='9 threads\nduration:0.005\nstart:2017-03-24 08:26:52\nend:2017-03-24 08:26:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2094.018px;'title='8 threads\nduration:0.009\nstart:2017-03-24 08:26:52\nend:2017-03-24 08:26:53'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2094.484px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:26:53\nend:2017-03-24 08:26:53'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2094.498px;'title='8 threads\nduration:0.065\nstart:2017-03-24 08:26:53\nend:2017-03-24 08:26:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2097.765px;'title='7 threads\nduration:0.032\nstart:2017-03-24 08:26:57\nend:2017-03-24 08:26:59'></div><div class='bar' style='background-color:#90BBD7;height:41.307px;width:120.000px;left:420px; top:2099.357px;'title='6 threads\nduration:0.866\nstart:2017-03-24 08:26:59\nend:2017-03-24 08:27:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2142.664px;'title='5 threads\nduration:0.000\nstart:2017-03-24 08:27:50\nend:2017-03-24 08:27:51'></div><div class='bar' style='background-color:#90BBD7;height:5.692px;width:120.000px;left:420px; top:2142.678px;'title='6 threads\nduration:0.154\nstart:2017-03-24 08:27:51\nend:2017-03-24 08:28:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2150.370px;'title='5 threads\nduration:0.000\nstart:2017-03-24 08:28:00\nend:2017-03-24 08:28:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2150.384px;'title='6 threads\nduration:0.015\nstart:2017-03-24 08:28:00\nend:2017-03-24 08:28:01'></div><div class='bar' style='background-color:#90BBD7;height:6.371px;width:100.000px;left:420px; top:2151.146px;'title='5 threads\nduration:0.167\nstart:2017-03-24 08:28:01\nend:2017-03-24 08:28:11'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2159.517px;'title='4 threads\nduration:0.091\nstart:2017-03-24 08:28:11\nend:2017-03-24 08:28:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:2164.082px;'title='3 threads\nduration:0.051\nstart:2017-03-24 08:28:16\nend:2017-03-24 08:28:19'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:2166.636px;'title='2 threads\nduration:0.003\nstart:2017-03-24 08:28:19\nend:2017-03-24 08:28:19'></div><div class='bar' style='background-color:#90BBD7;height:118.607px;width:20.000px;left:420px; top:2166.765px;'title='1 threads\nduration:2.412\nstart:2017-03-24 08:28:19\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2287.372px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2287.385px;'title='1 threads\nduration:0.043\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:47'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2289.512px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:30:47\nend:2017-03-24 08:30:47'></div><div class='bar' style='background-color:#90BBD7;height:11.071px;width:20.000px;left:420px; top:2289.527px;'title='1 threads\nduration:0.261\nstart:2017-03-24 08:30:47\nend:2017-03-24 08:31:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2302.597px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:31:02\nend:2017-03-24 08:31:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2302.611px;'title='1 threads\nduration:0.011\nstart:2017-03-24 08:31:02\nend:2017-03-24 08:31:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2303.156px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:31:03\nend:2017-03-24 08:31:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2303.169px;'title='1 threads\nduration:0.014\nstart:2017-03-24 08:31:03\nend:2017-03-24 08:31:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2303.861px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:31:04\nend:2017-03-24 08:31:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2303.876px;'title='1 threads\nduration:0.007\nstart:2017-03-24 08:31:04\nend:2017-03-24 08:31:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2304.209px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:31:04\nend:2017-03-24 08:31:04'></div><p class='time' style='top:198px;left:420px;'>Threads</p><div class='bar' style='background-color:#03969D;height:21.350px;width:0.000px;left:420px; top:219.975px;'title='0 threads\nduration:0.467\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:243.325px;'title='1 threads\nduration:0.094\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:57'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:0.000px;left:420px; top:248.005px;'title='0 threads\nduration:0.000\nstart:2017-03-24 07:49:57\nend:2017-03-24 07:49:57'></div><div class='bar' style='background-color:#03969D;height:914.694px;width:20.000px;left:420px; top:248.020px;'title='1 threads\nduration:18.334\nstart:2017-03-24 07:49:57\nend:2017-03-24 08:08:17'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:0.000px;left:420px; top:1164.714px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:08:17\nend:2017-03-24 08:08:17'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:1164.728px;'title='1 threads\nduration:0.081\nstart:2017-03-24 08:08:17\nend:2017-03-24 08:08:22'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:0.000px;left:420px; top:1168.778px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:08:22\nend:2017-03-24 08:08:22'></div><div class='bar' style='background-color:#03969D;height:912.560px;width:20.000px;left:420px; top:1168.791px;'title='1 threads\nduration:18.291\nstart:2017-03-24 08:08:22\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:0.000px;left:420px; top:2083.351px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2083.366px;'title='1 threads\nduration:0.001\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2083.409px;'title='2 threads\nduration:0.001\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:39'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2083.457px;'title='3 threads\nduration:0.081\nstart:2017-03-24 08:26:39\nend:2017-03-24 08:26:44'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2087.487px;'title='2 threads\nduration:0.001\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:44'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2087.551px;'title='3 threads\nduration:0.060\nstart:2017-03-24 08:26:44\nend:2017-03-24 08:26:48'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2090.576px;'title='2 threads\nduration:0.047\nstart:2017-03-24 08:26:48\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2092.923px;'title='1 threads\nduration:0.004\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2093.100px;'title='2 threads\nduration:0.002\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:51'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2093.178px;'title='3 threads\nduration:0.012\nstart:2017-03-24 08:26:51\nend:2017-03-24 08:26:52'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2093.768px;'title='2 threads\nduration:0.000\nstart:2017-03-24 08:26:52\nend:2017-03-24 08:26:52'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2093.782px;'title='3 threads\nduration:0.005\nstart:2017-03-24 08:26:52\nend:2017-03-24 08:26:52'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2094.018px;'title='2 threads\nduration:0.009\nstart:2017-03-24 08:26:52\nend:2017-03-24 08:26:53'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2094.484px;'title='1 threads\nduration:0.066\nstart:2017-03-24 08:26:53\nend:2017-03-24 08:26:57'></div><div class='bar' style='background-color:#03969D;height:187.621px;width:0.000px;left:420px; top:2097.765px;'title='0 threads\nduration:3.792\nstart:2017-03-24 08:26:57\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2287.385px;'title='1 threads\nduration:0.043\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:47'></div><div class='bar' style='background-color:#03969D;height:12.697px;width:0.000px;left:420px; top:2289.512px;'title='0 threads\nduration:0.294\nstart:2017-03-24 08:30:47\nend:2017-03-24 08:31:04'></div>\n        </div>\n    </body>"
  },
  {
    "path": "docs/source/_static/bold-1subject-8tasks.html",
    "content": "<!DOCTYPE html>\n    <head>\n        <style>\n            #content{\n                width:99%;\n                height:100%;\n                position:absolute;\n            }\n\n            .node{\n                background-color:#7070FF;\n                border-radius: 5px;\n                position:absolute;\n                width:20px;\n                white-space:pre-wrap;\n            }\n\n            .line{\n                position: absolute;\n                color: #C2C2C2;\n                opacity: 0.5;\n                margin: 0px;\n            }\n\n            .time{\n                position: absolute;\n                font-size: 16px;\n                color: #666666;\n                margin: 0px;\n            }\n\n            .bar{\n                position: absolute;\n                height: 1px;\n                opacity: 0.7;\n            }\n\n            .dot{\n                position: absolute;\n                width: 1px;\n                height: 1px;\n                background-color: red;\n            }\n            .label {\n                width:20px;\n                height:20px;\n                opacity: 0.7;\n                display: inline-block;\n            }\n        </style>\n    </head>\n\n    <body>\n        <div id=\"content\">\n            <div style=\"display:inline-block;\">\n    <p>Start: 2017-03-24 07:47:15</p><p>Finish: 2017-03-24 08:34:22</p><p>Duration: 47.11 minutes</p><p>Nodes: 264</p><p>Cores: 10</p>\n    </div>\n    <div style=\"display:inline-block;margin-left:60px;vertical-align: top;\">\n        <p><span><div class=\"label\" style=\"background-color:#90BBD7;\"></div> Estimated Resource</span></p>\n        <p><span><div class=\"label\" style=\"background-color:#03969D;\"></div> Actual Resource</span></p>\n        <p><span><div class=\"label\" style=\"background-color:#f00;\"></div> Failed Node</span></p>\n    </div>\n    <hr class='line' width='98%' style='top:220px;'><p class='time' style='top:200px;'> 07:47 </p><hr class='line' width='98%' style='top:720px;'><p class='time' style='top:700px;'> 07:57 </p><hr class='line' width='98%' style='top:1220px;'><p class='time' style='top:1200px;'> 08:07 </p><hr class='line' width='98%' style='top:1720px;'><p class='time' style='top:1700px;'> 08:17 </p><hr class='line' width='98%' style='top:2220px;'><p class='time' style='top:2200px;'> 08:27 </p><hr class='line' width='98%' style='top:2720px;'><p class='time' style='top:2700px;'> 08:37 </p><div class='node' style='left:60px;top:220.0px;height:3px;background-color:#2D2D66;'title='metadata\nduration:0.01883305\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:16'></div><div class='node' style='left:90px;top:220.123085px;height:3px;background-color:#2D2D66;'title='metadata\nduration:0.0193509833333\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:17'></div><div class='node' style='left:60px;top:220.138063333px;height:3px;background-color:#9B9BFF;'title='metadata\nduration:0.0216515166667\nstart:2017-03-24 07:47:16\nend:2017-03-24 07:47:17'></div><div class='node' style='left:120px;top:220.050640833px;height:3px;background-color:#2D2D66;'title='metadata\nduration:0.0271726666667\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:17'></div><div class='node' style='left:150px;top:220.10193px;height:3px;background-color:#7070FF;'title='metadata\nduration:0.0288994333333\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:17'></div><div class='node' style='left:60px;top:221.264195833px;height:3px;background-color:#9B9BFF;'title='metadata\nduration:0.0142606666667\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:18'></div><div class='node' style='left:90px;top:221.573648333px;height:3px;background-color:#4E4EB2;'title='metadata\nduration:0.0162747166667\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:18'></div><div class='node' style='left:60px;top:222.000908333px;height:3px;background-color:#4E4EB2;'title='metadata\nduration:0.0125192833333\nstart:2017-03-24 07:47:18\nend:2017-03-24 07:47:19'></div><div class='node' style='left:120px;top:221.276653333px;height:4.61742166667px;background-color:#2D2D66;'title='reorient_and_discard\nduration:0.132348433333\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:25'></div><div class='node' style='left:60px;top:227.9182525px;height:3px;background-color:#2D2D66;'title='get_mean_RPI\nduration:0.0386848333333\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:27'></div><div class='node' style='left:90px;top:228.009711667px;height:3px;background-color:#7070FF;'title='SpikesMask\nduration:0.0966519166667\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:31'></div><div class='node' style='left:180px;top:220.034540833px;height:10.9752291667px;background-color:#2D2D66;'title='reorient_and_discard\nduration:0.259504583333\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:31'></div><div class='node' style='left:60px;top:232.867849167px;height:3px;background-color:#9B9BFF;'title='SpikesFinderBgMask\nduration:0.0387990333333\nstart:2017-03-24 07:47:31\nend:2017-03-24 07:47:33'></div><div class='node' style='left:210px;top:220.066673333px;height:13.169495px;background-color:#7070FF;'title='reorient_and_discard\nduration:0.3033899\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:34'></div><div class='node' style='left:60px;top:234.831385833px;height:3px;background-color:#2D2D66;'title='get_mean_RPI\nduration:0.04619875\nstart:2017-03-24 07:47:33\nend:2017-03-24 07:47:36'></div><div class='node' style='left:240px;top:220.083805833px;height:15.3237558333px;background-color:#4E4EB2;'title='reorient_and_discard\nduration:0.346475116667\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:36'></div><div class='node' style='left:90px;top:233.036415px;height:3.640545px;background-color:#7070FF;'title='SpikesMask\nduration:0.1128109\nstart:2017-03-24 07:47:31\nend:2017-03-24 07:47:38'></div><div class='node' style='left:150px;top:221.587125833px;height:17.6209191667px;background-color:#9B9BFF;'title='reorient_and_discard\nduration:0.392418383333\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:41'></div><div class='node' style='left:60px;top:238.701144167px;height:3px;background-color:#9B9BFF;'title='get_mean_RPI\nduration:0.0580746166667\nstart:2017-03-24 07:47:38\nend:2017-03-24 07:47:41'></div><div class='node' style='left:270px;top:220.021133333px;height:20.2119883333px;background-color:#7070FF;'title='reorient_and_discard\nduration:0.444239766667\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:42'></div><div class='node' style='left:300px;top:221.250526667px;height:19.9143116667px;background-color:#7070FF;'title='reorient_and_discard\nduration:0.438286233333\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:43'></div><div class='node' style='left:120px;top:237.165245px;height:4.155295px;background-color:#9B9BFF;'title='SpikesMask\nduration:0.1231059\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:47:43'></div><div class='node' style='left:330px;top:220.154740833px;height:21.5185816667px;background-color:#4E4EB2;'title='reorient_and_discard\nduration:0.470371633333\nstart:2017-03-24 07:47:16\nend:2017-03-24 07:47:44'></div><div class='node' style='left:60px;top:241.627785833px;height:3px;background-color:#4E4EB2;'title='SpikesFinderBgMask\nduration:0.06056545\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:47:45'></div><div class='node' style='left:90px;top:243.1903075px;height:3px;background-color:#4E4EB2;'title='get_mean_RPI\nduration:0.0749494833333\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:48'></div><div class='node' style='left:60px;top:244.67968px;height:3px;background-color:#4E4EB2;'title='get_mean_RPI\nduration:0.0814991\nstart:2017-03-24 07:47:45\nend:2017-03-24 07:47:50'></div><div class='node' style='left:120px;top:243.346355px;height:5.00521333333px;background-color:#2D2D66;'title='SpikesMask\nduration:0.140104266667\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:52'></div><div class='node' style='left:60px;top:250.373894167px;height:3.7836725px;background-color:#4E4EB2;'title='SpikesMask\nduration:0.11567345\nstart:2017-03-24 07:47:52\nend:2017-03-24 07:47:59'></div><div class='node' style='left:60px;top:256.179930833px;height:3px;background-color:#4E4EB2;'title='SpikesFinderBgMask\nduration:0.0751501333333\nstart:2017-03-24 07:47:59\nend:2017-03-24 07:48:03'></div><div class='node' style='left:60px;top:227.965924167px;height:31.8136708333px;background-color:#9B9BFF;'title='SpikesFinderFFT\nduration:0.676273416667\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:48:05'></div><div class='node' style='left:180px;top:235.259100833px;height:63.4382133333px;background-color:#2D2D66;'title='SpikesFinderFFT\nduration:1.30876426667\nstart:2017-03-24 07:47:34\nend:2017-03-24 07:48:52'></div><div class='node' style='left:60px;top:300.7234075px;height:4.63683333333px;background-color:#9B9BFF;'title='SpikesMask\nduration:0.132736666667\nstart:2017-03-24 07:48:52\nend:2017-03-24 07:49:00'></div><div class='node' style='left:60px;top:307.385069167px;height:3px;background-color:#9B9BFF;'title='PlotSpikes\nduration:0.00795851666667\nstart:2017-03-24 07:49:00\nend:2017-03-24 07:49:01'></div><div class='node' style='left:60px;top:307.8076px;height:3px;background-color:#2D2D66;'title='get_mean_RPI\nduration:0.0544828666667\nstart:2017-03-24 07:49:01\nend:2017-03-24 07:49:04'></div><div class='node' style='left:60px;top:310.560500833px;height:3px;background-color:#9B9BFF;'title='PlotSpikes\nduration:0.00791591666667\nstart:2017-03-24 07:49:04\nend:2017-03-24 07:49:04'></div><div class='node' style='left:150px;top:241.233929167px;height:77.23808px;background-color:#2D2D66;'title='SpikesFinderFFT\nduration:1.5847616\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:49:16'></div><div class='node' style='left:60px;top:320.498953333px;height:3px;background-color:#4E4EB2;'title='PlotSpikes\nduration:0.00795425\nstart:2017-03-24 07:49:16\nend:2017-03-24 07:49:16'></div><div class='node' style='left:210px;top:237.4303325px;height:87.24076px;background-color:#7070FF;'title='SpikesFinderFFT\nduration:1.7848152\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:49:23'></div><div class='node' style='left:60px;top:326.71102px;height:3px;background-color:#9B9BFF;'title='PlotSpikes\nduration:0.00993461666667\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:24'></div><div class='node' style='left:60px;top:327.2464975px;height:3px;background-color:#2D2D66;'title='SpikesFinderBgMask\nduration:0.0991952333333\nstart:2017-03-24 07:49:24\nend:2017-03-24 07:49:30'></div><div class='node' style='left:60px;top:332.243993333px;height:3px;background-color:#9B9BFF;'title='SpikesFinderBgMask\nduration:0.0982847833333\nstart:2017-03-24 07:49:30\nend:2017-03-24 07:49:36'></div><div class='node' style='left:60px;top:337.197405833px;height:4.94157666667px;background-color:#2D2D66;'title='SpikesMask\nduration:0.138831533333\nstart:2017-03-24 07:49:36\nend:2017-03-24 07:49:44'></div><div class='node' style='left:60px;top:344.180239167px;height:3.167675px;background-color:#4E4EB2;'title='SpikesFinderBgMask\nduration:0.1033535\nstart:2017-03-24 07:49:44\nend:2017-03-24 07:49:51'></div><div class='node' style='left:60px;top:349.38698px;height:3px;background-color:#2D2D66;'title='SpikesFinderBgMask\nduration:0.0695209666667\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:55'></div><div class='node' style='left:60px;top:352.902603333px;height:4.21911916667px;background-color:#9B9BFF;'title='SpikesMask\nduration:0.124382383333\nstart:2017-03-24 07:49:55\nend:2017-03-24 07:50:02'></div><div class='node' style='left:60px;top:359.160541667px;height:3px;background-color:#2D2D66;'title='SpikesFinderBgMask\nduration:0.0908492666667\nstart:2017-03-24 07:50:02\nend:2017-03-24 07:50:08'></div><div class='node' style='left:240px;top:243.6978575px;height:119.8094875px;background-color:#7070FF;'title='SpikesFinderFFT\nduration:2.43618975\nstart:2017-03-24 07:47:44\nend:2017-03-24 07:50:10'></div><div class='node' style='left:60px;top:365.546733333px;height:3px;background-color:#7070FF;'title='PlotSpikes\nduration:0.00924476666667\nstart:2017-03-24 07:50:10\nend:2017-03-24 07:50:11'></div><div class='node' style='left:270px;top:242.256461667px;height:124.329404167px;background-color:#2D2D66;'title='SpikesFinderFFT\nduration:2.52658808333\nstart:2017-03-24 07:47:42\nend:2017-03-24 07:50:14'></div><div class='node' style='left:60px;top:368.610995833px;height:3px;background-color:#9B9BFF;'title='PlotSpikes\nduration:0.0079781\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:14'></div><div class='node' style='left:90px;top:366.034241667px;height:3px;background-color:#4E4EB2;'title='get_mean_RPI\nduration:0.0708486\nstart:2017-03-24 07:50:11\nend:2017-03-24 07:50:15'></div><div class='node' style='left:60px;top:369.036465833px;height:3px;background-color:#9B9BFF;'title='get_mean_RPI\nduration:0.07570185\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:19'></div><div class='node' style='left:300px;top:246.9629425px;height:129.084185px;background-color:#2D2D66;'title='SpikesFinderFFT\nduration:2.6216837\nstart:2017-03-24 07:47:48\nend:2017-03-24 07:50:25'></div><div class='node' style='left:60px;top:378.074125px;height:3px;background-color:#2D2D66;'title='PlotSpikes\nduration:0.0078804\nstart:2017-03-24 07:50:25\nend:2017-03-24 07:50:26'></div><div class='node' style='left:120px;top:363.7439075px;height:111.230088333px;background-color:#2D2D66;'title='SpikesFinderFFT\nduration:2.26460176667\nstart:2017-03-24 07:50:08\nend:2017-03-24 07:52:24'></div><div class='node' style='left:60px;top:476.998126667px;height:3px;background-color:#7070FF;'title='PlotSpikes\nduration:0.00790336666667\nstart:2017-03-24 07:52:24\nend:2017-03-24 07:52:24'></div><div class='node' style='left:60px;top:229.877399167px;height:275.53074px;background-color:#9B9BFF;'title='motion_correct\nduration:5.5506148\nstart:2017-03-24 07:47:27\nend:2017-03-24 07:53:00'></div><div class='node' style='left:60px;top:507.433969167px;height:3px;background-color:#2D2D66;'title='get_mean_motion\nduration:0.02924885\nstart:2017-03-24 07:53:00\nend:2017-03-24 07:53:02'></div><div class='node' style='left:60px;top:508.920789167px;height:275.136178333px;background-color:#7070FF;'title='motion_correct_A\nduration:5.54272356667\nstart:2017-03-24 07:53:02\nend:2017-03-24 07:58:35'></div><div class='node' style='left:60px;top:786.083316667px;height:3px;background-color:#2D2D66;'title='ComputeFD\nduration:0.00701798333333\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='node' style='left:60px;top:786.460014167px;height:3px;background-color:#2D2D66;'title='mean\nduration:0.0293037833333\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:37'></div><div class='node' style='left:90px;top:786.1847175px;height:3px;background-color:#2D2D66;'title='compute_tsnr\nduration:0.0437663833333\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:37'></div><div class='node' style='left:120px;top:786.133671667px;height:3px;background-color:#2D2D66;'title='quality\nduration:0.0495236\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:38'></div><div class='node' style='left:330px;top:259.961636667px;height:534.040775px;background-color:#2D2D66;'title='motion_correct\nduration:10.7208155\nstart:2017-03-24 07:48:03\nend:2017-03-24 07:58:47'></div><div class='node' style='left:60px;top:796.02696px;height:3px;background-color:#7070FF;'title='get_mean_motion\nduration:0.0443699166667\nstart:2017-03-24 07:58:47\nend:2017-03-24 07:58:49'></div><div class='node' style='left:90px;top:788.635954167px;height:61.7528708333px;background-color:#9B9BFF;'title='PlotMosaicMean\nduration:1.27505741667\nstart:2017-03-24 07:58:38\nend:2017-03-24 07:59:54'></div><div class='node' style='left:150px;top:788.398566667px;height:62.5489575px;background-color:#2D2D66;'title='PlotMosaicSD\nduration:1.29097915\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:59:55'></div><div class='node' style='left:180px;top:787.951671667px;height:63.7645px;background-color:#2D2D66;'title='PlotMosaicNoise\nduration:1.31529\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:59:56'></div><div class='node' style='left:60px;top:852.970749167px;height:3px;background-color:#2D2D66;'title='afni_msk\nduration:0.0655470666667\nstart:2017-03-24 07:59:55\nend:2017-03-24 07:59:59'></div><div class='node' style='left:60px;top:856.299193333px;height:3px;background-color:#7070FF;'title='smoothness\nduration:0.0140577666667\nstart:2017-03-24 07:59:59\nend:2017-03-24 08:00:00'></div><div class='node' style='left:90px;top:852.41336px;height:3.40176666667px;background-color:#7070FF;'title='SharpenEPI\nduration:0.108035333333\nstart:2017-03-24 07:59:54\nend:2017-03-24 08:00:01'></div><div class='node' style='left:60px;top:857.837075833px;height:3px;background-color:#7070FF;'title='EPIApplyMask\nduration:0.0130487833333\nstart:2017-03-24 08:00:01\nend:2017-03-24 08:00:02'></div><div class='node' style='left:120px;top:857.0271525px;height:3px;background-color:#9B9BFF;'title='outliers\nduration:0.04400625\nstart:2017-03-24 08:00:00\nend:2017-03-24 08:00:02'></div><div class='node' style='left:150px;top:856.380691667px;height:41.9662116667px;background-color:#9B9BFF;'title='ComputeDVARS\nduration:0.879324233333\nstart:2017-03-24 07:59:59\nend:2017-03-24 08:00:52'></div><div class='node' style='left:60px;top:900.368750833px;height:3px;background-color:#2D2D66;'title='measures\nduration:0.0468902666667\nstart:2017-03-24 08:00:52\nend:2017-03-24 08:00:55'></div><div class='node' style='left:60px;top:902.7355625px;height:3px;background-color:#9B9BFF;'title='datasink\nduration:0.0186427333333\nstart:2017-03-24 08:00:55\nend:2017-03-24 08:00:56'></div><div class='node' style='left:60px;top:903.6914875px;height:5.42459666667px;background-color:#7070FF;'title='PlotBrainmask\nduration:0.148491933333\nstart:2017-03-24 08:00:56\nend:2017-03-24 08:01:05'></div><div class='node' style='left:90px;top:859.251110833px;height:63.91066px;background-color:#7070FF;'title='PlotMosaicZoomed\nduration:1.3182132\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:01:22'></div><div class='node' style='left:60px;top:320.923105px;height:646.506943333px;background-color:#9B9BFF;'title='motion_correct\nduration:12.9701388667\nstart:2017-03-24 07:49:16\nend:2017-03-24 08:02:15'></div><div class='node' style='left:60px;top:969.4525025px;height:3px;background-color:#4E4EB2;'title='get_mean_motion\nduration:0.0502383\nstart:2017-03-24 08:02:15\nend:2017-03-24 08:02:18'></div><div class='node' style='left:60px;top:261.804943333px;height:729.134924167px;background-color:#4E4EB2;'title='motion_correct\nduration:14.6226984833\nstart:2017-03-24 07:48:06\nend:2017-03-24 08:02:43'></div><div class='node' style='left:60px;top:992.96283px;height:3px;background-color:#4E4EB2;'title='get_mean_motion\nduration:0.0552318666667\nstart:2017-03-24 08:02:43\nend:2017-03-24 08:02:46'></div><div class='node' style='left:120px;top:858.51324px;height:195.196524167px;background-color:#9B9BFF;'title='EPI2MNI\nduration:3.94393048333\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:03:58'></div><div class='node' style='left:60px;top:1055.731725px;height:3px;background-color:#2D2D66;'title='ResampleSegmentation\nduration:0.0426837166667\nstart:2017-03-24 08:03:58\nend:2017-03-24 08:04:01'></div><div class='node' style='left:60px;top:1057.8884325px;height:8.26594333333px;background-color:#2D2D66;'title='BigPlot\nduration:0.205318866667\nstart:2017-03-24 08:04:01\nend:2017-03-24 08:04:13'></div><div class='node' style='left:60px;top:1068.17858333px;height:3px;background-color:#7070FF;'title='MergePlots\nduration:0.0111009333333\nstart:2017-03-24 08:04:13\nend:2017-03-24 08:04:14'></div><div class='node' style='left:60px;top:1068.7576425px;height:3px;background-color:#9B9BFF;'title='GenerateReport\nduration:0.01407005\nstart:2017-03-24 08:04:14\nend:2017-03-24 08:04:15'></div><div class='node' style='left:60px;top:1069.48501417px;height:3px;background-color:#4E4EB2;'title='dsplots\nduration:0.00664276666667\nstart:2017-03-24 08:04:15\nend:2017-03-24 08:04:15'></div><div class='node' style='left:210px;top:369.601205833px;height:854.371371667px;background-color:#9B9BFF;'title='motion_correct\nduration:17.1274274333\nstart:2017-03-24 07:50:15\nend:2017-03-24 08:07:23'></div><div class='node' style='left:60px;top:1225.99629583px;height:3px;background-color:#2D2D66;'title='get_mean_motion\nduration:0.0653174833333\nstart:2017-03-24 08:07:23\nend:2017-03-24 08:07:26'></div><div class='node' style='left:60px;top:310.983531667px;height:942.004583333px;background-color:#9B9BFF;'title='motion_correct\nduration:18.8800916667\nstart:2017-03-24 07:49:05\nend:2017-03-24 08:07:57'></div><div class='node' style='left:60px;top:1255.01212417px;height:3px;background-color:#9B9BFF;'title='get_mean_motion\nduration:0.0713258166667\nstart:2017-03-24 08:07:57\nend:2017-03-24 08:08:02'></div><div class='node' style='left:60px;top:248.779181667px;height:1024.27941167px;background-color:#7070FF;'title='motion_correct\nduration:20.5255882333\nstart:2017-03-24 07:47:50\nend:2017-03-24 08:08:21'></div><div class='node' style='left:60px;top:1275.0825675px;height:3px;background-color:#7070FF;'title='get_mean_motion\nduration:0.0779818833333\nstart:2017-03-24 08:08:21\nend:2017-03-24 08:08:26'></div><div class='node' style='left:240px;top:372.8963525px;height:945.1552975px;background-color:#7070FF;'title='motion_correct\nduration:18.94310595\nstart:2017-03-24 07:50:19\nend:2017-03-24 08:09:15'></div><div class='node' style='left:60px;top:1320.07581083px;height:3px;background-color:#7070FF;'title='get_mean_motion\nduration:0.0712962666667\nstart:2017-03-24 08:09:15\nend:2017-03-24 08:09:20'></div><div class='node' style='left:270px;top:798.269268333px;height:532.289664167px;background-color:#9B9BFF;'title='motion_correct_A\nduration:10.6857932833\nstart:2017-03-24 07:58:49\nend:2017-03-24 08:09:30'></div><div class='node' style='left:60px;top:1332.63019083px;height:3px;background-color:#4E4EB2;'title='ComputeFD\nduration:0.00844258333333\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:31'></div><div class='node' style='left:60px;top:1333.07575333px;height:3px;background-color:#7070FF;'title='mean\nduration:0.0435475666667\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:34'></div><div class='node' style='left:90px;top:1332.7248125px;height:3px;background-color:#4E4EB2;'title='compute_tsnr\nduration:0.0575677333333\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:34'></div><div class='node' style='left:120px;top:1332.58256333px;height:3px;background-color:#2D2D66;'title='quality\nduration:0.0775077166667\nstart:2017-03-24 08:09:30\nend:2017-03-24 08:09:35'></div><div class='node' style='left:150px;top:1332.67762917px;height:3.43578px;background-color:#4E4EB2;'title='afni_msk\nduration:0.1087156\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:37'></div><div class='node' style='left:60px;top:1335.27549417px;height:3px;background-color:#9B9BFF;'title='SharpenEPI\nduration:0.09389295\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:39'></div><div class='node' style='left:60px;top:1339.9942925px;height:3px;background-color:#9B9BFF;'title='smoothness\nduration:0.0124585833333\nstart:2017-03-24 08:09:39\nend:2017-03-24 08:09:40'></div><div class='node' style='left:90px;top:1338.13687333px;height:3px;background-color:#7070FF;'title='outliers\nduration:0.0556898666667\nstart:2017-03-24 08:09:37\nend:2017-03-24 08:09:40'></div><div class='node' style='left:60px;top:1340.6410775px;height:3px;background-color:#7070FF;'title='EPIApplyMask\nduration:0.0117885333333\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:41'></div><div class='node' style='left:90px;top:1340.94564583px;height:5.66274416667px;background-color:#2D2D66;'title='PlotBrainmask\nduration:0.153254883333\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:50'></div><div class='node' style='left:180px;top:1335.62661333px;height:63.1776791667px;background-color:#4E4EB2;'title='PlotMosaicSD\nduration:1.30355358333\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:10:52'></div><div class='node' style='left:120px;top:1336.48172333px;height:68.729555px;background-color:#2D2D66;'title='PlotMosaicMean\nduration:1.4145911\nstart:2017-03-24 08:09:35\nend:2017-03-24 08:11:00'></div><div class='node' style='left:60px;top:1348.63208833px;height:63.7773425px;background-color:#9B9BFF;'title='PlotMosaicZoomed\nduration:1.31554685\nstart:2017-03-24 08:09:50\nend:2017-03-24 08:11:09'></div><div class='node' style='left:90px;top:1407.234305px;height:36.9666083333px;background-color:#7070FF;'title='ComputeDVARS\nduration:0.779332166667\nstart:2017-03-24 08:11:00\nend:2017-03-24 08:11:47'></div><div class='node' style='left:60px;top:1446.22567083px;height:3px;background-color:#9B9BFF;'title='measures\nduration:0.07258695\nstart:2017-03-24 08:11:47\nend:2017-03-24 08:11:51'></div><div class='node' style='left:60px;top:1449.88076583px;height:3px;background-color:#7070FF;'title='datasink\nduration:0.0186686833333\nstart:2017-03-24 08:11:51\nend:2017-03-24 08:11:52'></div><div class='node' style='left:150px;top:1400.83342917px;height:62.3446333333px;background-color:#7070FF;'title='PlotMosaicNoise\nduration:1.28689266667\nstart:2017-03-24 08:10:52\nend:2017-03-24 08:12:10'></div><div class='node' style='left:210px;top:1341.25465583px;height:186.598824167px;background-color:#4E4EB2;'title='EPI2MNI\nduration:3.77197648333\nstart:2017-03-24 08:09:41\nend:2017-03-24 08:13:27'></div><div class='node' style='left:60px;top:1529.87695667px;height:3px;background-color:#9B9BFF;'title='ResampleSegmentation\nduration:0.0190309666667\nstart:2017-03-24 08:13:27\nend:2017-03-24 08:13:28'></div><div class='node' style='left:60px;top:1530.85202167px;height:8.3071175px;background-color:#4E4EB2;'title='BigPlot\nduration:0.20614235\nstart:2017-03-24 08:13:28\nend:2017-03-24 08:13:41'></div><div class='node' style='left:60px;top:1541.18347px;height:3px;background-color:#7070FF;'title='MergePlots\nduration:0.01108555\nstart:2017-03-24 08:13:41\nend:2017-03-24 08:13:41'></div><div class='node' style='left:60px;top:1541.76114083px;height:3px;background-color:#7070FF;'title='GenerateReport\nduration:0.0132546666667\nstart:2017-03-24 08:13:41\nend:2017-03-24 08:13:42'></div><div class='node' style='left:60px;top:1542.44775083px;height:3px;background-color:#2D2D66;'title='dsplots\nduration:0.00659173333333\nstart:2017-03-24 08:13:42\nend:2017-03-24 08:13:43'></div><div class='node' style='left:300px;top:971.988043333px;height:641.581163333px;background-color:#7070FF;'title='motion_correct_A\nduration:12.8716232667\nstart:2017-03-24 08:02:18\nend:2017-03-24 08:15:10'></div><div class='node' style='left:60px;top:1615.68892667px;height:3px;background-color:#9B9BFF;'title='ComputeFD\nduration:0.00860868333333\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:11'></div><div class='node' style='left:90px;top:1615.63888833px;height:3px;background-color:#9B9BFF;'title='mean\nduration:0.0519073333333\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:13'></div><div class='node' style='left:120px;top:1615.5937725px;height:3px;background-color:#7070FF;'title='compute_tsnr\nduration:0.0667648833333\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:14'></div><div class='node' style='left:150px;top:1615.7876125px;height:3px;background-color:#4E4EB2;'title='quality\nduration:0.0917751833333\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:16'></div><div class='node' style='left:180px;top:1615.73962417px;height:4.33192px;background-color:#7070FF;'title='afni_msk\nduration:0.1266384\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:18'></div><div class='node' style='left:60px;top:1622.09518417px;height:3px;background-color:#4E4EB2;'title='outliers\nduration:0.0611221333333\nstart:2017-03-24 08:15:18\nend:2017-03-24 08:15:22'></div><div class='node' style='left:90px;top:1620.40127083px;height:3px;background-color:#2D2D66;'title='SharpenEPI\nduration:0.0983551166667\nstart:2017-03-24 08:15:16\nend:2017-03-24 08:15:22'></div><div class='node' style='left:60px;top:1625.17485833px;height:6.17726333333px;background-color:#7070FF;'title='PlotBrainmask\nduration:0.163545266667\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:31'></div><div class='node' style='left:60px;top:1633.37585083px;height:3px;background-color:#7070FF;'title='smoothness\nduration:0.0124261333333\nstart:2017-03-24 08:15:31\nend:2017-03-24 08:15:32'></div><div class='node' style='left:60px;top:1634.0207775px;height:44.9233391667px;background-color:#4E4EB2;'title='ComputeDVARS\nduration:0.938466783333\nstart:2017-03-24 08:15:32\nend:2017-03-24 08:16:28'></div><div class='node' style='left:210px;top:1618.25782917px;height:62.7088308333px;background-color:#2D2D66;'title='PlotMosaicMean\nduration:1.29417661667\nstart:2017-03-24 08:15:13\nend:2017-03-24 08:16:31'></div><div class='node' style='left:240px;top:1618.30640333px;height:63.0659875px;background-color:#2D2D66;'title='PlotMosaicNoise\nduration:1.30131975\nstart:2017-03-24 08:15:13\nend:2017-03-24 08:16:31'></div><div class='node' style='left:60px;top:1682.9902375px;height:3px;background-color:#2D2D66;'title='EPIApplyMask\nduration:0.0131444166667\nstart:2017-03-24 08:16:31\nend:2017-03-24 08:16:32'></div><div class='node' style='left:120px;top:1618.956955px;height:63.5520733333px;background-color:#7070FF;'title='PlotMosaicSD\nduration:1.31104146667\nstart:2017-03-24 08:15:14\nend:2017-03-24 08:16:33'></div><div class='node' style='left:90px;top:1680.96566083px;height:3px;background-color:#7070FF;'title='measures\nduration:0.0855391833333\nstart:2017-03-24 08:16:29\nend:2017-03-24 08:16:34'></div><div class='node' style='left:60px;top:1685.26681833px;height:3px;background-color:#4E4EB2;'title='datasink\nduration:0.0215412333333\nstart:2017-03-24 08:16:34\nend:2017-03-24 08:16:35'></div><div class='node' style='left:150px;top:1625.34265667px;height:63.2540191667px;background-color:#9B9BFF;'title='PlotMosaicZoomed\nduration:1.30508038333\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:16:40'></div><div class='node' style='left:330px;top:995.748403333px;height:729.194174167px;background-color:#7070FF;'title='motion_correct_A\nduration:14.6238834833\nstart:2017-03-24 08:02:46\nend:2017-03-24 08:17:24'></div><div class='node' style='left:60px;top:1726.96397px;height:3px;background-color:#2D2D66;'title='ComputeFD\nduration:0.00880528333333\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='node' style='left:60px;top:1727.00913667px;height:3px;background-color:#2D2D66;'title='mean\nduration:0.0585441833333\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:27'></div><div class='node' style='left:90px;top:1727.106815px;height:3px;background-color:#9B9BFF;'title='compute_tsnr\nduration:0.0721693166667\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:28'></div><div class='node' style='left:120px;top:1727.05574917px;height:3.14159083333px;background-color:#4E4EB2;'title='quality\nduration:0.102831816667\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:30'></div><div class='node' style='left:150px;top:1727.15758167px;height:4.9889775px;background-color:#9B9BFF;'title='afni_msk\nduration:0.13977955\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:32'></div><div class='node' style='left:60px;top:1730.73850833px;height:3px;background-color:#2D2D66;'title='SharpenEPI\nduration:0.09432675\nstart:2017-03-24 08:17:28\nend:2017-03-24 08:17:34'></div><div class='node' style='left:90px;top:1734.16957833px;height:6.13466666667px;background-color:#2D2D66;'title='PlotBrainmask\nduration:0.162693333333\nstart:2017-03-24 08:17:32\nend:2017-03-24 08:17:42'></div><div class='node' style='left:60px;top:1742.32634917px;height:3px;background-color:#7070FF;'title='EPIApplyMask\nduration:0.01220555\nstart:2017-03-24 08:17:42\nend:2017-03-24 08:17:43'></div><div class='node' style='left:120px;top:1732.22002167px;height:63.33957px;background-color:#2D2D66;'title='PlotMosaicSD\nduration:1.3067914\nstart:2017-03-24 08:17:30\nend:2017-03-24 08:18:48'></div><div class='node' style='left:180px;top:1729.9592975px;height:66.900375px;background-color:#9B9BFF;'title='PlotMosaicMean\nduration:1.3780075\nstart:2017-03-24 08:17:27\nend:2017-03-24 08:18:50'></div><div class='node' style='left:210px;top:1730.00659833px;height:67.3611833333px;background-color:#4E4EB2;'title='PlotMosaicNoise\nduration:1.38722366667\nstart:2017-03-24 08:17:27\nend:2017-03-24 08:18:51'></div><div class='node' style='left:60px;top:1798.8823625px;height:3px;background-color:#4E4EB2;'title='smoothness\nduration:0.0132283666667\nstart:2017-03-24 08:18:50\nend:2017-03-24 08:18:51'></div><div class='node' style='left:150px;top:1735.47839667px;height:62.8296416667px;background-color:#2D2D66;'title='PlotMosaicZoomed\nduration:1.29659283333\nstart:2017-03-24 08:17:34\nend:2017-03-24 08:18:52'></div><div class='node' style='left:90px;top:1797.58046667px;height:3px;background-color:#2D2D66;'title='outliers\nduration:0.0656870833333\nstart:2017-03-24 08:18:48\nend:2017-03-24 08:18:52'></div><div class='node' style='left:60px;top:1799.39055px;height:37.0878758333px;background-color:#9B9BFF;'title='ComputeDVARS\nduration:0.781757516667\nstart:2017-03-24 08:18:51\nend:2017-03-24 08:19:38'></div><div class='node' style='left:60px;top:1838.4997925px;height:3px;background-color:#2D2D66;'title='measures\nduration:0.0948319833333\nstart:2017-03-24 08:19:38\nend:2017-03-24 08:19:43'></div><div class='node' style='left:60px;top:1843.26432083px;height:3px;background-color:#7070FF;'title='datasink\nduration:0.0188895666667\nstart:2017-03-24 08:19:43\nend:2017-03-24 08:19:44'></div><div class='node' style='left:240px;top:1683.67058417px;height:193.7993975px;background-color:#7070FF;'title='EPI2MNI\nduration:3.91598795\nstart:2017-03-24 08:16:32\nend:2017-03-24 08:20:27'></div><div class='node' style='left:60px;top:1879.49331083px;height:3px;background-color:#2D2D66;'title='ResampleSegmentation\nduration:0.0182332666667\nstart:2017-03-24 08:20:27\nend:2017-03-24 08:20:28'></div><div class='node' style='left:60px;top:1880.428175px;height:8.09360083333px;background-color:#9B9BFF;'title='BigPlot\nduration:0.201872016667\nstart:2017-03-24 08:20:28\nend:2017-03-24 08:20:40'></div><div class='node' style='left:60px;top:1890.54459417px;height:3px;background-color:#4E4EB2;'title='MergePlots\nduration:0.0110805333333\nstart:2017-03-24 08:20:40\nend:2017-03-24 08:20:41'></div><div class='node' style='left:60px;top:1891.12394167px;height:3px;background-color:#7070FF;'title='GenerateReport\nduration:0.0134452333333\nstart:2017-03-24 08:20:41\nend:2017-03-24 08:20:42'></div><div class='node' style='left:60px;top:1891.82294583px;height:3px;background-color:#9B9BFF;'title='dsplots\nduration:0.00661203333333\nstart:2017-03-24 08:20:42\nend:2017-03-24 08:20:42'></div><div class='node' style='left:270px;top:1742.959835px;height:184.394176667px;background-color:#4E4EB2;'title='EPI2MNI\nduration:3.72788353333\nstart:2017-03-24 08:17:43\nend:2017-03-24 08:21:27'></div><div class='node' style='left:60px;top:1929.38300833px;height:3px;background-color:#7070FF;'title='ResampleSegmentation\nduration:0.0182611666667\nstart:2017-03-24 08:21:27\nend:2017-03-24 08:21:28'></div><div class='node' style='left:60px;top:1930.32245167px;height:9.35977166667px;background-color:#4E4EB2;'title='BigPlot\nduration:0.227195433333\nstart:2017-03-24 08:21:28\nend:2017-03-24 08:21:41'></div><div class='node' style='left:60px;top:1941.70500417px;height:3px;background-color:#7070FF;'title='MergePlots\nduration:0.0110828\nstart:2017-03-24 08:21:41\nend:2017-03-24 08:21:42'></div><div class='node' style='left:60px;top:1942.28199667px;height:3px;background-color:#4E4EB2;'title='GenerateReport\nduration:0.01242815\nstart:2017-03-24 08:21:42\nend:2017-03-24 08:21:43'></div><div class='node' style='left:60px;top:1942.92431583px;height:3px;background-color:#4E4EB2;'title='dsplots\nduration:0.00656725\nstart:2017-03-24 08:21:43\nend:2017-03-24 08:21:43'></div><div class='node' style='left:60px;top:1229.28587833px;height:844.176498333px;background-color:#7070FF;'title='motion_correct_A\nduration:16.9235299667\nstart:2017-03-24 08:07:26\nend:2017-03-24 08:24:22'></div><div class='node' style='left:60px;top:2075.53422083px;height:3px;background-color:#4E4EB2;'title='ComputeFD\nduration:0.00949731666667\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:23'></div><div class='node' style='left:90px;top:2075.62838167px;height:3px;background-color:#7070FF;'title='mean\nduration:0.0665872833333\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:26'></div><div class='node' style='left:120px;top:2075.67642917px;height:3px;background-color:#9B9BFF;'title='compute_tsnr\nduration:0.0804294333333\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:27'></div><div class='node' style='left:150px;top:2075.4862675px;height:3.85961916667px;background-color:#2D2D66;'title='quality\nduration:0.117192383333\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:29'></div><div class='node' style='left:180px;top:2075.58270167px;height:5.95947416667px;background-color:#2D2D66;'title='afni_msk\nduration:0.159189483333\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:32'></div><div class='node' style='left:60px;top:2079.02851333px;height:3px;background-color:#2D2D66;'title='SharpenEPI\nduration:0.0971956666667\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:32'></div><div class='node' style='left:60px;top:2083.73587333px;height:3px;background-color:#4E4EB2;'title='smoothness\nduration:0.0125364333333\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:33'></div><div class='node' style='left:90px;top:2083.9123425px;height:3px;background-color:#9B9BFF;'title='EPIApplyMask\nduration:0.0125178333333\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:33'></div><div class='node' style='left:120px;top:2083.65078417px;height:3px;background-color:#4E4EB2;'title='outliers\nduration:0.0744182333333\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:36'></div><div class='node' style='left:60px;top:2084.38614px;height:6.11388333333px;background-color:#9B9BFF;'title='PlotBrainmask\nduration:0.162277666667\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:42'></div><div class='node' style='left:150px;top:2083.5663425px;height:44.085635px;background-color:#7070FF;'title='ComputeDVARS\nduration:0.9217127\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:25:27'></div><div class='node' style='left:60px;top:2129.676175px;height:3.3598925px;background-color:#7070FF;'title='measures\nduration:0.10719785\nstart:2017-03-24 08:25:27\nend:2017-03-24 08:25:33'></div><div class='node' style='left:60px;top:2135.06049167px;height:3px;background-color:#7070FF;'title='datasink\nduration:0.0186641\nstart:2017-03-24 08:25:33\nend:2017-03-24 08:25:35'></div><div class='node' style='left:210px;top:2078.98087583px;height:89.5876808333px;background-color:#7070FF;'title='PlotMosaicMean\nduration:1.83175361667\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:26:16'></div><div class='node' style='left:240px;top:2079.07368667px;height:90.47018px;background-color:#9B9BFF;'title='PlotMosaicNoise\nduration:1.8494036\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:26:17'></div><div class='node' style='left:270px;top:2079.7204125px;height:91.299515px;background-color:#9B9BFF;'title='PlotMosaicSD\nduration:1.8659903\nstart:2017-03-24 08:24:27\nend:2017-03-24 08:26:19'></div><div class='node' style='left:90px;top:2087.3948325px;height:86.7817241667px;background-color:#2D2D66;'title='PlotMosaicZoomed\nduration:1.77563448333\nstart:2017-03-24 08:24:36\nend:2017-03-24 08:26:23'></div><div class='node' style='left:60px;top:1258.60095083px;height:937.982015833px;background-color:#4E4EB2;'title='motion_correct_A\nduration:18.7996403167\nstart:2017-03-24 08:08:02\nend:2017-03-24 08:26:50'></div><div class='node' style='left:60px;top:2198.74861px;height:3px;background-color:#7070FF;'title='ComputeFD\nduration:0.008681\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='node' style='left:60px;top:2198.60596917px;height:3px;background-color:#9B9BFF;'title='mean\nduration:0.0715045\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:54'></div><div class='node' style='left:90px;top:2198.69789917px;height:3px;background-color:#7070FF;'title='compute_tsnr\nduration:0.0869013\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:55'></div><div class='node' style='left:120px;top:2198.79910917px;height:4.41722083333px;background-color:#7070FF;'title='quality\nduration:0.128344416667\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:58'></div><div class='node' style='left:60px;top:2202.32012833px;height:3px;background-color:#7070FF;'title='SharpenEPI\nduration:0.0987725166667\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:27:00'></div><div class='node' style='left:150px;top:2198.65102px;height:6.80140416667px;background-color:#2D2D66;'title='afni_msk\nduration:0.176028083333\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:27:00'></div><div class='node' style='left:60px;top:2207.64717833px;height:3px;background-color:#4E4EB2;'title='EPIApplyMask\nduration:0.0123537833333\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:01'></div><div class='node' style='left:90px;top:2207.48034833px;height:3px;background-color:#9B9BFF;'title='outliers\nduration:0.07998485\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:05'></div><div class='node' style='left:60px;top:2211.50256917px;height:3px;background-color:#2D2D66;'title='smoothness\nduration:0.0125085166667\nstart:2017-03-24 08:27:05\nend:2017-03-24 08:27:06'></div><div class='node' style='left:120px;top:2207.72768px;height:8.37714583333px;background-color:#4E4EB2;'title='PlotBrainmask\nduration:0.207542916667\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:13'></div><div class='node' style='left:60px;top:2212.15006583px;height:40.8525233333px;background-color:#7070FF;'title='ComputeDVARS\nduration:0.857050466667\nstart:2017-03-24 08:27:06\nend:2017-03-24 08:27:57'></div><div class='node' style='left:60px;top:2255.03005833px;height:3.82315px;background-color:#2D2D66;'title='measures\nduration:0.116463\nstart:2017-03-24 08:27:57\nend:2017-03-24 08:28:04'></div><div class='node' style='left:60px;top:2260.87663px;height:3px;background-color:#9B9BFF;'title='datasink\nduration:0.0191353666667\nstart:2017-03-24 08:28:04\nend:2017-03-24 08:28:06'></div><div class='node' style='left:60px;top:1323.66512333px;height:937.653236667px;background-color:#4E4EB2;'title='motion_correct_A\nduration:18.7930647333\nstart:2017-03-24 08:09:20\nend:2017-03-24 08:28:07'></div><div class='node' style='left:60px;top:2263.34190833px;height:3px;background-color:#2D2D66;'title='ComputeFD\nduration:0.00854203333333\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:08'></div><div class='node' style='left:90px;top:2263.43666083px;height:3px;background-color:#7070FF;'title='compute_tsnr\nduration:0.0861494666667\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:13'></div><div class='node' style='left:60px;top:2263.79145917px;height:4.41101916667px;background-color:#4E4EB2;'title='quality\nduration:0.128220383333\nstart:2017-03-24 08:28:08\nend:2017-03-24 08:28:16'></div><div class='node' style='left:120px;top:2263.38961583px;height:6.76794px;background-color:#7070FF;'title='afni_msk\nduration:0.1753588\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:18'></div><div class='node' style='left:60px;top:2270.22613667px;height:3px;background-color:#4E4EB2;'title='mean\nduration:0.08249955\nstart:2017-03-24 08:28:16\nend:2017-03-24 08:28:21'></div><div class='node' style='left:90px;top:2272.17805333px;height:3px;background-color:#9B9BFF;'title='outliers\nduration:0.0791227333333\nstart:2017-03-24 08:28:18\nend:2017-03-24 08:28:23'></div><div class='node' style='left:180px;top:2084.5621925px;height:190.828580833px;background-color:#7070FF;'title='EPI2MNI\nduration:3.85657161667\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:28:24'></div><div class='node' style='left:60px;top:2277.41885917px;height:3px;background-color:#4E4EB2;'title='SharpenEPI\nduration:0.0984097833333\nstart:2017-03-24 08:28:24\nend:2017-03-24 08:28:30'></div><div class='node' style='left:60px;top:2282.36746833px;height:3px;background-color:#4E4EB2;'title='EPIApplyMask\nduration:0.0125666666667\nstart:2017-03-24 08:28:30\nend:2017-03-24 08:28:31'></div><div class='node' style='left:210px;top:2202.26395833px;height:90.1118258333px;background-color:#7070FF;'title='PlotMosaicNoise\nduration:1.84223651667\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:28:45'></div><div class='node' style='left:240px;top:2203.073305px;height:92.2872075px;background-color:#4E4EB2;'title='PlotMosaicSD\nduration:1.88574415\nstart:2017-03-24 08:26:55\nend:2017-03-24 08:28:48'></div><div class='node' style='left:270px;top:2202.21237333px;height:95.4729058333px;background-color:#4E4EB2;'title='PlotMosaicMean\nduration:1.94945811667\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:28:51'></div><div class='node' style='left:60px;top:2299.71089px;height:3px;background-color:#2D2D66;'title='smoothness\nduration:0.01669735\nstart:2017-03-24 08:28:51\nend:2017-03-24 08:28:52'></div><div class='node' style='left:60px;top:1279.00890917px;height:1021.83258417px;background-color:#4E4EB2;'title='motion_correct_A\nduration:20.4766516833\nstart:2017-03-24 08:08:26\nend:2017-03-24 08:28:55'></div><div class='node' style='left:150px;top:2207.56399583px;height:94.1197583333px;background-color:#2D2D66;'title='PlotMosaicZoomed\nduration:1.92239516667\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:28:56'></div><div class='node' style='left:60px;top:2302.87396167px;height:3px;background-color:#7070FF;'title='ComputeFD\nduration:0.0176355333333\nstart:2017-03-24 08:28:55\nend:2017-03-24 08:28:56'></div><div class='node' style='left:60px;top:2303.82613667px;height:3px;background-color:#7070FF;'title='compute_tsnr\nduration:0.09343715\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:29:02'></div><div class='node' style='left:90px;top:2300.57984083px;height:10.6914416667px;background-color:#7070FF;'title='PlotBrainmask\nduration:0.253828833333\nstart:2017-03-24 08:28:52\nend:2017-03-24 08:29:07'></div><div class='node' style='left:120px;top:2303.7787775px;height:7.85972416667px;background-color:#2D2D66;'title='afni_msk\nduration:0.197194483333\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:29:08'></div><div class='node' style='left:60px;top:2313.29447667px;height:3px;background-color:#7070FF;'title='mean\nduration:0.0779364333333\nstart:2017-03-24 08:29:07\nend:2017-03-24 08:29:12'></div><div class='node' style='left:60px;top:2317.2143975px;height:3px;background-color:#2D2D66;'title='outliers\nduration:0.0854284166667\nstart:2017-03-24 08:29:12\nend:2017-03-24 08:29:17'></div><div class='node' style='left:300px;top:2274.37419833px;height:54.32916px;background-color:#7070FF;'title='ComputeDVARS\nduration:1.1265832\nstart:2017-03-24 08:28:21\nend:2017-03-24 08:29:28'></div><div class='node' style='left:60px;top:2321.51212667px;height:8.03086px;background-color:#4E4EB2;'title='PlotBrainmask\nduration:0.2006172\nstart:2017-03-24 08:29:17\nend:2017-03-24 08:29:29'></div><div class='node' style='left:60px;top:2331.56526583px;height:3px;background-color:#9B9BFF;'title='smoothness\nduration:0.0132657833333\nstart:2017-03-24 08:29:29\nend:2017-03-24 08:29:30'></div><div class='node' style='left:60px;top:2332.25076417px;height:3.03271333333px;background-color:#9B9BFF;'title='SharpenEPI\nduration:0.100654266667\nstart:2017-03-24 08:29:30\nend:2017-03-24 08:29:36'></div><div class='node' style='left:60px;top:2337.31168583px;height:3px;background-color:#2D2D66;'title='EPIApplyMask\nduration:0.0130699166667\nstart:2017-03-24 08:29:36\nend:2017-03-24 08:29:37'></div><div class='node' style='left:330px;top:2267.77281833px;height:90.1987475px;background-color:#4E4EB2;'title='PlotMosaicSD\nduration:1.84397495\nstart:2017-03-24 08:28:13\nend:2017-03-24 08:30:03'></div><div class='node' style='left:90px;top:2313.66194833px;height:47.3015241667px;background-color:#7070FF;'title='ComputeDVARS\nduration:0.986030483333\nstart:2017-03-24 08:29:08\nend:2017-03-24 08:30:07'></div><div class='node' style='left:60px;top:2362.98733667px;height:4.471205px;background-color:#7070FF;'title='measures\nduration:0.1294241\nstart:2017-03-24 08:30:07\nend:2017-03-24 08:30:15'></div><div class='node' style='left:150px;top:2308.52367833px;height:63.8613941667px;background-color:#9B9BFF;'title='PlotMosaicSD\nduration:1.31722788333\nstart:2017-03-24 08:29:02\nend:2017-03-24 08:30:21'></div><div class='node' style='left:60px;top:2369.48151px;height:4.9807475px;background-color:#2D2D66;'title='quality\nduration:0.13961495\nstart:2017-03-24 08:30:15\nend:2017-03-24 08:30:23'></div><div class='node' style='left:60px;top:2376.48362833px;height:3px;background-color:#9B9BFF;'title='datasink\nduration:0.0194484666667\nstart:2017-03-24 08:30:23\nend:2017-03-24 08:30:24'></div><div class='node' style='left:60px;top:2276.15842167px;height:101.235704167px;background-color:#2D2D66;'title='PlotMosaicNoise\nduration:2.06471408333\nstart:2017-03-24 08:28:23\nend:2017-03-24 08:30:27'></div><div class='node' style='left:60px;top:2379.41588667px;height:3px;background-color:#7070FF;'title='ResampleSegmentation\nduration:0.0191597166667\nstart:2017-03-24 08:30:27\nend:2017-03-24 08:30:28'></div><div class='node' style='left:90px;top:2377.4776825px;height:4.5796525px;background-color:#4E4EB2;'title='measures\nduration:0.13159305\nstart:2017-03-24 08:30:24\nend:2017-03-24 08:30:32'></div><div class='node' style='left:60px;top:2384.07957333px;height:3px;background-color:#9B9BFF;'title='datasink\nduration:0.0195198333333\nstart:2017-03-24 08:30:32\nend:2017-03-24 08:30:33'></div><div class='node' style='left:180px;top:2294.39866667px;height:94.1409758333px;background-color:#4E4EB2;'title='PlotMosaicZoomed\nduration:1.92281951667\nstart:2017-03-24 08:28:45\nend:2017-03-24 08:30:40'></div><div class='node' style='left:120px;top:2380.396155px;height:11.1715533333px;background-color:#2D2D66;'title='BigPlot\nduration:0.263431066667\nstart:2017-03-24 08:30:28\nend:2017-03-24 08:30:44'></div><div class='node' style='left:60px;top:2393.59004917px;height:3px;background-color:#4E4EB2;'title='MergePlots\nduration:0.0110801\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:44'></div><div class='node' style='left:210px;top:2297.38340667px;height:95.1132241667px;background-color:#7070FF;'title='PlotMosaicMean\nduration:1.94226448333\nstart:2017-03-24 08:28:48\nend:2017-03-24 08:30:45'></div><div class='node' style='left:60px;top:2394.166195px;height:3px;background-color:#9B9BFF;'title='GenerateReport\nduration:0.0143445833333\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:45'></div><div class='node' style='left:60px;top:2394.9053325px;height:3px;background-color:#4E4EB2;'title='dsplots\nduration:0.00662083333333\nstart:2017-03-24 08:30:45\nend:2017-03-24 08:30:46'></div><div class='node' style='left:240px;top:2330.7236725px;height:66.9675175px;background-color:#2D2D66;'title='PlotMosaicZoomed\nduration:1.37935035\nstart:2017-03-24 08:29:28\nend:2017-03-24 08:30:51'></div><div class='node' style='left:60px;top:2208.28732px;height:193.991030833px;background-color:#9B9BFF;'title='EPI2MNI\nduration:3.91982061667\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:30:56'></div><div class='node' style='left:60px;top:2404.29991167px;height:3px;background-color:#9B9BFF;'title='ResampleSegmentation\nduration:0.0190379166667\nstart:2017-03-24 08:30:57\nend:2017-03-24 08:30:58'></div><div class='node' style='left:270px;top:2337.9858525px;height:67.0233041667px;background-color:#4E4EB2;'title='PlotMosaicNoise\nduration:1.38046608333\nstart:2017-03-24 08:29:37\nend:2017-03-24 08:31:00'></div><div class='node' style='left:60px;top:2405.27394833px;height:12.0061841667px;background-color:#9B9BFF;'title='BigPlot\nduration:0.280123683333\nstart:2017-03-24 08:30:58\nend:2017-03-24 08:31:14'></div><div class='node' style='left:60px;top:2419.30328417px;height:3px;background-color:#4E4EB2;'title='MergePlots\nduration:0.0110060833333\nstart:2017-03-24 08:31:15\nend:2017-03-24 08:31:15'></div><div class='node' style='left:60px;top:2419.8752675px;height:3px;background-color:#7070FF;'title='GenerateReport\nduration:0.0132616333333\nstart:2017-03-24 08:31:15\nend:2017-03-24 08:31:16'></div><div class='node' style='left:60px;top:2420.55998083px;height:3px;background-color:#2D2D66;'title='dsplots\nduration:0.00660645\nstart:2017-03-24 08:31:16\nend:2017-03-24 08:31:16'></div><div class='node' style='left:300px;top:2359.99397833px;height:63.5178441667px;background-color:#9B9BFF;'title='PlotMosaicMean\nduration:1.31035688333\nstart:2017-03-24 08:30:03\nend:2017-03-24 08:31:22'></div><div class='node' style='left:60px;top:2283.01907333px;height:189.577879167px;background-color:#7070FF;'title='EPI2MNI\nduration:3.83155758333\nstart:2017-03-24 08:28:31\nend:2017-03-24 08:32:21'></div><div class='node' style='left:60px;top:2474.61854px;height:3px;background-color:#4E4EB2;'title='ResampleSegmentation\nduration:0.0241236166667\nstart:2017-03-24 08:32:21\nend:2017-03-24 08:32:22'></div><div class='node' style='left:60px;top:2475.84608px;height:10.1899725px;background-color:#2D2D66;'title='BigPlot\nduration:0.24379945\nstart:2017-03-24 08:32:22\nend:2017-03-24 08:32:37'></div><div class='node' style='left:60px;top:2488.05759167px;height:3px;background-color:#4E4EB2;'title='MergePlots\nduration:0.0111012166667\nstart:2017-03-24 08:32:37\nend:2017-03-24 08:32:38'></div><div class='node' style='left:60px;top:2488.63998667px;height:3px;background-color:#7070FF;'title='GenerateReport\nduration:0.0141620333333\nstart:2017-03-24 08:32:38\nend:2017-03-24 08:32:39'></div><div class='node' style='left:60px;top:2489.36966333px;height:3px;background-color:#9B9BFF;'title='dsplots\nduration:0.00662411666667\nstart:2017-03-24 08:32:39\nend:2017-03-24 08:32:39'></div><div class='node' style='left:150px;top:2374.40862167px;height:183.835910833px;background-color:#9B9BFF;'title='EPI2MNI\nduration:3.71671821667\nstart:2017-03-24 08:30:21\nend:2017-03-24 08:34:04'></div><div class='node' style='left:60px;top:2560.26421917px;height:3px;background-color:#7070FF;'title='ResampleSegmentation\nduration:0.0190589\nstart:2017-03-24 08:34:04\nend:2017-03-24 08:34:05'></div><div class='node' style='left:60px;top:2561.23832083px;height:10.6064691667px;background-color:#9B9BFF;'title='BigPlot\nduration:0.252129383333\nstart:2017-03-24 08:34:05\nend:2017-03-24 08:34:20'></div><div class='node' style='left:60px;top:2573.86625083px;height:3px;background-color:#2D2D66;'title='MergePlots\nduration:0.0109989\nstart:2017-03-24 08:34:20\nend:2017-03-24 08:34:21'></div><div class='node' style='left:60px;top:2574.43587917px;height:3px;background-color:#4E4EB2;'title='GenerateReport\nduration:0.0131955333333\nstart:2017-03-24 08:34:21\nend:2017-03-24 08:34:21'></div><div class='node' style='left:60px;top:2575.116845px;height:3px;background-color:#7070FF;'title='dsplots\nduration:0.00662518333333\nstart:2017-03-24 08:34:21\nend:2017-03-24 08:34:22'></div><p class='time' style='top:198px;left:960px;'>Memory</p><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:220.000px;'title='1.000 GB\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:220.021px;'title='2.000 GB\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:220.035px;'title='3.000 GB\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:220.051px;'title='4.000 GB\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:220.067px;'title='5.000 GB\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:220.084px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:220.102px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:220.123px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:220.138px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:16\nend:2017-03-24 07:47:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:220.155px;'title='10.000 GB\nduration:0.016\nstart:2017-03-24 07:47:16\nend:2017-03-24 07:47:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:220.942px;'title='9.000 GB\nduration:0.003\nstart:2017-03-24 07:47:16\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:221.091px;'title='8.000 GB\nduration:0.003\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:221.221px;'title='7.000 GB\nduration:0.001\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:221.251px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:221.264px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:221.277px;'title='10.000 GB\nduration:0.003\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:221.409px;'title='9.000 GB\nduration:0.003\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:221.547px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:221.574px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:221.587px;'title='10.000 GB\nduration:0.008\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:221.977px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:18\nend:2017-03-24 07:47:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:222.001px;'title='10.000 GB\nduration:0.008\nstart:2017-03-24 07:47:18\nend:2017-03-24 07:47:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:222.387px;'title='9.000 GB\nduration:0.005\nstart:2017-03-24 07:47:18\nend:2017-03-24 07:47:19'></div><div class='bar' style='background-color:#90BBD7;height:3.267px;width:160.000px;left:960px; top:222.627px;'title='8.000 GB\nduration:0.105\nstart:2017-03-24 07:47:19\nend:2017-03-24 07:47:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:227.894px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:227.918px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:227.966px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:228.010px;'title='10.000 GB\nduration:0.037\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:229.852px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:27\nend:2017-03-24 07:47:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:229.877px;'title='10.000 GB\nduration:0.059\nstart:2017-03-24 07:47:27\nend:2017-03-24 07:47:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:232.842px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:47:31\nend:2017-03-24 07:47:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:232.868px;'title='10.000 GB\nduration:0.003\nstart:2017-03-24 07:47:31\nend:2017-03-24 07:47:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:233.010px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:47:31\nend:2017-03-24 07:47:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:233.036px;'title='10.000 GB\nduration:0.035\nstart:2017-03-24 07:47:31\nend:2017-03-24 07:47:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:234.808px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:33\nend:2017-03-24 07:47:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:234.831px;'title='10.000 GB\nduration:0.008\nstart:2017-03-24 07:47:33\nend:2017-03-24 07:47:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:235.236px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:34\nend:2017-03-24 07:47:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:235.259px;'title='10.000 GB\nduration:0.038\nstart:2017-03-24 07:47:34\nend:2017-03-24 07:47:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:237.141px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:47:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:237.165px;'title='10.000 GB\nduration:0.005\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:47:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:237.408px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:47:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:237.430px;'title='10.000 GB\nduration:0.025\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:47:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:238.677px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:38\nend:2017-03-24 07:47:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:238.701px;'title='10.000 GB\nduration:0.050\nstart:2017-03-24 07:47:38\nend:2017-03-24 07:47:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:241.208px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:47:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:241.234px;'title='10.000 GB\nduration:0.007\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:47:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:241.605px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:47:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:241.628px;'title='10.000 GB\nduration:0.012\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:47:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:242.233px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:42\nend:2017-03-24 07:47:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:242.256px;'title='10.000 GB\nduration:0.018\nstart:2017-03-24 07:47:42\nend:2017-03-24 07:47:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:243.165px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:243.190px;'title='10.000 GB\nduration:0.003\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:243.321px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:243.346px;'title='10.000 GB\nduration:0.007\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:243.673px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:44\nend:2017-03-24 07:47:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:243.698px;'title='10.000 GB\nduration:0.019\nstart:2017-03-24 07:47:44\nend:2017-03-24 07:47:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:244.656px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:45\nend:2017-03-24 07:47:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:244.680px;'title='10.000 GB\nduration:0.045\nstart:2017-03-24 07:47:45\nend:2017-03-24 07:47:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:246.938px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:47:48\nend:2017-03-24 07:47:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:246.963px;'title='10.000 GB\nduration:0.036\nstart:2017-03-24 07:47:48\nend:2017-03-24 07:47:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:248.755px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:50\nend:2017-03-24 07:47:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:248.779px;'title='10.000 GB\nduration:0.031\nstart:2017-03-24 07:47:50\nend:2017-03-24 07:47:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:250.352px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:52\nend:2017-03-24 07:47:52'></div><div class='bar' style='background-color:#90BBD7;height:3.784px;width:200.000px;left:960px; top:250.374px;'title='10.000 GB\nduration:0.116\nstart:2017-03-24 07:47:52\nend:2017-03-24 07:47:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:256.158px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:47:59\nend:2017-03-24 07:47:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:256.180px;'title='10.000 GB\nduration:0.075\nstart:2017-03-24 07:47:59\nend:2017-03-24 07:48:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:259.937px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:48:03\nend:2017-03-24 07:48:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:259.962px;'title='10.000 GB\nduration:0.036\nstart:2017-03-24 07:48:03\nend:2017-03-24 07:48:05'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:261.780px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:48:05\nend:2017-03-24 07:48:06'></div><div class='bar' style='background-color:#90BBD7;height:36.892px;width:200.000px;left:960px; top:261.805px;'title='10.000 GB\nduration:0.778\nstart:2017-03-24 07:48:06\nend:2017-03-24 07:48:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:300.697px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:48:52\nend:2017-03-24 07:48:52'></div><div class='bar' style='background-color:#90BBD7;height:4.637px;width:200.000px;left:960px; top:300.723px;'title='10.000 GB\nduration:0.133\nstart:2017-03-24 07:48:52\nend:2017-03-24 07:49:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:307.360px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:49:00\nend:2017-03-24 07:49:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:307.385px;'title='10.000 GB\nduration:0.008\nstart:2017-03-24 07:49:00\nend:2017-03-24 07:49:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:307.783px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:49:01\nend:2017-03-24 07:49:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:307.808px;'title='10.000 GB\nduration:0.054\nstart:2017-03-24 07:49:01\nend:2017-03-24 07:49:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:310.532px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:04\nend:2017-03-24 07:49:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:310.561px;'title='10.000 GB\nduration:0.008\nstart:2017-03-24 07:49:04\nend:2017-03-24 07:49:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:310.956px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:04\nend:2017-03-24 07:49:05'></div><div class='bar' style='background-color:#90BBD7;height:7.488px;width:200.000px;left:960px; top:310.984px;'title='10.000 GB\nduration:0.190\nstart:2017-03-24 07:49:05\nend:2017-03-24 07:49:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:320.472px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:16\nend:2017-03-24 07:49:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:320.499px;'title='10.000 GB\nduration:0.008\nstart:2017-03-24 07:49:16\nend:2017-03-24 07:49:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:320.897px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:16\nend:2017-03-24 07:49:16'></div><div class='bar' style='background-color:#90BBD7;height:3.748px;width:200.000px;left:960px; top:320.923px;'title='10.000 GB\nduration:0.115\nstart:2017-03-24 07:49:16\nend:2017-03-24 07:49:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:326.671px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:326.711px;'title='10.000 GB\nduration:0.010\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:327.208px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:24\nend:2017-03-24 07:49:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:327.246px;'title='10.000 GB\nduration:0.099\nstart:2017-03-24 07:49:24\nend:2017-03-24 07:49:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:332.206px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:30\nend:2017-03-24 07:49:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:332.244px;'title='10.000 GB\nduration:0.098\nstart:2017-03-24 07:49:30\nend:2017-03-24 07:49:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:337.158px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:36\nend:2017-03-24 07:49:36'></div><div class='bar' style='background-color:#90BBD7;height:4.942px;width:200.000px;left:960px; top:337.197px;'title='10.000 GB\nduration:0.139\nstart:2017-03-24 07:49:36\nend:2017-03-24 07:49:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:344.139px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:44\nend:2017-03-24 07:49:44'></div><div class='bar' style='background-color:#90BBD7;height:3.168px;width:200.000px;left:960px; top:344.180px;'title='10.000 GB\nduration:0.103\nstart:2017-03-24 07:49:44\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:349.348px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:349.387px;'title='10.000 GB\nduration:0.070\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:352.863px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:49:55\nend:2017-03-24 07:49:55'></div><div class='bar' style='background-color:#90BBD7;height:4.219px;width:200.000px;left:960px; top:352.903px;'title='10.000 GB\nduration:0.124\nstart:2017-03-24 07:49:55\nend:2017-03-24 07:50:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:359.122px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:50:02\nend:2017-03-24 07:50:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:359.161px;'title='10.000 GB\nduration:0.091\nstart:2017-03-24 07:50:02\nend:2017-03-24 07:50:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:363.703px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:50:08\nend:2017-03-24 07:50:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:363.744px;'title='10.000 GB\nduration:0.035\nstart:2017-03-24 07:50:08\nend:2017-03-24 07:50:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:365.507px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:50:10\nend:2017-03-24 07:50:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:365.547px;'title='10.000 GB\nduration:0.009\nstart:2017-03-24 07:50:10\nend:2017-03-24 07:50:11'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:366.009px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:50:11\nend:2017-03-24 07:50:11'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:366.034px;'title='10.000 GB\nduration:0.051\nstart:2017-03-24 07:50:11\nend:2017-03-24 07:50:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:368.586px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:368.611px;'title='10.000 GB\nduration:0.008\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:369.010px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:369.036px;'title='10.000 GB\nduration:0.011\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:369.577px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:50:15\nend:2017-03-24 07:50:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:369.601px;'title='10.000 GB\nduration:0.064\nstart:2017-03-24 07:50:15\nend:2017-03-24 07:50:19'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:372.822px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:50:19\nend:2017-03-24 07:50:19'></div><div class='bar' style='background-color:#90BBD7;height:3.151px;width:200.000px;left:960px; top:372.896px;'title='10.000 GB\nduration:0.103\nstart:2017-03-24 07:50:19\nend:2017-03-24 07:50:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:378.047px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:50:25\nend:2017-03-24 07:50:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:378.074px;'title='10.000 GB\nduration:0.008\nstart:2017-03-24 07:50:25\nend:2017-03-24 07:50:26'></div><div class='bar' style='background-color:#90BBD7;height:96.506px;width:180.000px;left:960px; top:378.468px;'title='9.000 GB\nduration:1.970\nstart:2017-03-24 07:50:26\nend:2017-03-24 07:52:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:476.974px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 07:52:24\nend:2017-03-24 07:52:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:476.998px;'title='9.000 GB\nduration:0.008\nstart:2017-03-24 07:52:24\nend:2017-03-24 07:52:24'></div><div class='bar' style='background-color:#90BBD7;height:28.015px;width:160.000px;left:960px; top:477.393px;'title='8.000 GB\nduration:0.600\nstart:2017-03-24 07:52:24\nend:2017-03-24 07:53:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:507.408px;'title='7.000 GB\nduration:0.001\nstart:2017-03-24 07:53:00\nend:2017-03-24 07:53:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:507.434px;'title='8.000 GB\nduration:0.029\nstart:2017-03-24 07:53:00\nend:2017-03-24 07:53:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:508.896px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 07:53:02\nend:2017-03-24 07:53:02'></div><div class='bar' style='background-color:#90BBD7;height:275.136px;width:160.000px;left:960px; top:508.921px;'title='8.000 GB\nduration:5.543\nstart:2017-03-24 07:53:02\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:786.057px;'title='7.000 GB\nduration:0.001\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:786.083px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:786.134px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:786.185px;'title='10.000 GB\nduration:0.005\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:786.434px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:786.460px;'title='10.000 GB\nduration:0.029\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:787.925px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:58:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:787.952px;'title='10.000 GB\nduration:0.008\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:58:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:788.373px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:58:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:788.399px;'title='10.000 GB\nduration:0.004\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:58:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:788.610px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 07:58:38\nend:2017-03-24 07:58:38'></div><div class='bar' style='background-color:#90BBD7;height:5.366px;width:200.000px;left:960px; top:788.636px;'title='10.000 GB\nduration:0.147\nstart:2017-03-24 07:58:38\nend:2017-03-24 07:58:47'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:796.002px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:58:47\nend:2017-03-24 07:58:47'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:796.027px;'title='10.000 GB\nduration:0.044\nstart:2017-03-24 07:58:47\nend:2017-03-24 07:58:49'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:798.245px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:58:49\nend:2017-03-24 07:58:49'></div><div class='bar' style='background-color:#90BBD7;height:52.120px;width:200.000px;left:960px; top:798.269px;'title='10.000 GB\nduration:1.082\nstart:2017-03-24 07:58:49\nend:2017-03-24 07:59:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:852.389px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:59:54\nend:2017-03-24 07:59:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:852.413px;'title='10.000 GB\nduration:0.011\nstart:2017-03-24 07:59:54\nend:2017-03-24 07:59:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:852.948px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 07:59:55\nend:2017-03-24 07:59:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:852.971px;'title='10.000 GB\nduration:0.015\nstart:2017-03-24 07:59:55\nend:2017-03-24 07:59:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:853.716px;'title='9.000 GB\nduration:0.051\nstart:2017-03-24 07:59:56\nend:2017-03-24 07:59:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:856.248px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 07:59:59\nend:2017-03-24 07:59:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:856.299px;'title='9.000 GB\nduration:0.002\nstart:2017-03-24 07:59:59\nend:2017-03-24 07:59:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:856.381px;'title='10.000 GB\nduration:0.012\nstart:2017-03-24 07:59:59\nend:2017-03-24 08:00:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:857.002px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:00:00\nend:2017-03-24 08:00:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:857.027px;'title='10.000 GB\nduration:0.016\nstart:2017-03-24 08:00:00\nend:2017-03-24 08:00:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:857.815px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:00:01\nend:2017-03-24 08:00:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:857.837px;'title='10.000 GB\nduration:0.013\nstart:2017-03-24 08:00:01\nend:2017-03-24 08:00:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:858.490px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:00:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:858.513px;'title='10.000 GB\nduration:0.014\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:00:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:859.227px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:00:02'></div><div class='bar' style='background-color:#90BBD7;height:39.096px;width:200.000px;left:960px; top:859.251px;'title='10.000 GB\nduration:0.822\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:00:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:900.347px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:00:52\nend:2017-03-24 08:00:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:900.369px;'title='10.000 GB\nduration:0.047\nstart:2017-03-24 08:00:52\nend:2017-03-24 08:00:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:902.713px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:00:55\nend:2017-03-24 08:00:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:902.736px;'title='10.000 GB\nduration:0.019\nstart:2017-03-24 08:00:55\nend:2017-03-24 08:00:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:903.668px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:00:56\nend:2017-03-24 08:00:56'></div><div class='bar' style='background-color:#90BBD7;height:5.425px;width:200.000px;left:960px; top:903.691px;'title='10.000 GB\nduration:0.148\nstart:2017-03-24 08:00:56\nend:2017-03-24 08:01:05'></div><div class='bar' style='background-color:#90BBD7;height:12.046px;width:180.000px;left:960px; top:911.116px;'title='9.000 GB\nduration:0.281\nstart:2017-03-24 08:01:05\nend:2017-03-24 08:01:22'></div><div class='bar' style='background-color:#90BBD7;height:42.268px;width:160.000px;left:960px; top:925.162px;'title='8.000 GB\nduration:0.885\nstart:2017-03-24 08:01:22\nend:2017-03-24 08:02:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:969.430px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:02:15\nend:2017-03-24 08:02:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:969.453px;'title='8.000 GB\nduration:0.050\nstart:2017-03-24 08:02:15\nend:2017-03-24 08:02:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:971.964px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:02:18\nend:2017-03-24 08:02:18'></div><div class='bar' style='background-color:#90BBD7;height:18.952px;width:160.000px;left:960px; top:971.988px;'title='8.000 GB\nduration:0.419\nstart:2017-03-24 08:02:18\nend:2017-03-24 08:02:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:992.940px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:02:43\nend:2017-03-24 08:02:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:992.963px;'title='8.000 GB\nduration:0.055\nstart:2017-03-24 08:02:43\nend:2017-03-24 08:02:46'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:995.724px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:02:46\nend:2017-03-24 08:02:46'></div><div class='bar' style='background-color:#90BBD7;height:57.961px;width:160.000px;left:960px; top:995.748px;'title='8.000 GB\nduration:1.199\nstart:2017-03-24 08:02:46\nend:2017-03-24 08:03:58'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1055.710px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:03:58\nend:2017-03-24 08:03:58'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1055.732px;'title='8.000 GB\nduration:0.043\nstart:2017-03-24 08:03:58\nend:2017-03-24 08:04:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1057.866px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:04:01\nend:2017-03-24 08:04:01'></div><div class='bar' style='background-color:#90BBD7;height:8.266px;width:160.000px;left:960px; top:1057.888px;'title='8.000 GB\nduration:0.205\nstart:2017-03-24 08:04:01\nend:2017-03-24 08:04:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1068.154px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:04:13\nend:2017-03-24 08:04:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1068.179px;'title='8.000 GB\nduration:0.011\nstart:2017-03-24 08:04:13\nend:2017-03-24 08:04:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1068.734px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:04:14\nend:2017-03-24 08:04:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1068.758px;'title='8.000 GB\nduration:0.014\nstart:2017-03-24 08:04:14\nend:2017-03-24 08:04:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1069.461px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:04:15\nend:2017-03-24 08:04:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1069.485px;'title='8.000 GB\nduration:0.007\nstart:2017-03-24 08:04:15\nend:2017-03-24 08:04:15'></div><div class='bar' style='background-color:#90BBD7;height:154.155px;width:140.000px;left:960px; top:1069.817px;'title='7.000 GB\nduration:3.123\nstart:2017-03-24 08:04:15\nend:2017-03-24 08:07:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1225.973px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:07:23\nend:2017-03-24 08:07:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1225.996px;'title='7.000 GB\nduration:0.065\nstart:2017-03-24 08:07:23\nend:2017-03-24 08:07:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1229.262px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:07:26\nend:2017-03-24 08:07:26'></div><div class='bar' style='background-color:#90BBD7;height:23.702px;width:140.000px;left:960px; top:1229.286px;'title='7.000 GB\nduration:0.514\nstart:2017-03-24 08:07:26\nend:2017-03-24 08:07:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1254.988px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:07:57\nend:2017-03-24 08:07:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1255.012px;'title='7.000 GB\nduration:0.071\nstart:2017-03-24 08:07:57\nend:2017-03-24 08:08:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1258.578px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:08:02\nend:2017-03-24 08:08:02'></div><div class='bar' style='background-color:#90BBD7;height:14.458px;width:140.000px;left:960px; top:1258.601px;'title='7.000 GB\nduration:0.329\nstart:2017-03-24 08:08:02\nend:2017-03-24 08:08:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1275.059px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:08:21\nend:2017-03-24 08:08:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1275.083px;'title='7.000 GB\nduration:0.078\nstart:2017-03-24 08:08:21\nend:2017-03-24 08:08:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1278.982px;'title='6.000 GB\nduration:0.001\nstart:2017-03-24 08:08:26\nend:2017-03-24 08:08:26'></div><div class='bar' style='background-color:#90BBD7;height:39.043px;width:140.000px;left:960px; top:1279.009px;'title='7.000 GB\nduration:0.821\nstart:2017-03-24 08:08:26\nend:2017-03-24 08:09:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1320.052px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:09:15\nend:2017-03-24 08:09:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1320.076px;'title='7.000 GB\nduration:0.071\nstart:2017-03-24 08:09:15\nend:2017-03-24 08:09:20'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1323.641px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:09:20\nend:2017-03-24 08:09:20'></div><div class='bar' style='background-color:#90BBD7;height:6.894px;width:140.000px;left:960px; top:1323.665px;'title='7.000 GB\nduration:0.178\nstart:2017-03-24 08:09:20\nend:2017-03-24 08:09:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1332.559px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:09:30\nend:2017-03-24 08:09:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1332.583px;'title='7.000 GB\nduration:0.001\nstart:2017-03-24 08:09:30\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1332.630px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1332.678px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1332.725px;'title='10.000 GB\nduration:0.007\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1333.052px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1333.076px;'title='10.000 GB\nduration:0.044\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1335.253px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1335.275px;'title='10.000 GB\nduration:0.007\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1335.603px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1335.627px;'title='10.000 GB\nduration:0.017\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1336.458px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:09:35\nend:2017-03-24 08:09:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1336.482px;'title='10.000 GB\nduration:0.033\nstart:2017-03-24 08:09:35\nend:2017-03-24 08:09:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1338.113px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:09:37\nend:2017-03-24 08:09:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1338.137px;'title='10.000 GB\nduration:0.037\nstart:2017-03-24 08:09:37\nend:2017-03-24 08:09:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1339.970px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:09:39\nend:2017-03-24 08:09:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1339.994px;'title='10.000 GB\nduration:0.012\nstart:2017-03-24 08:09:39\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1340.617px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1340.641px;'title='10.000 GB\nduration:0.006\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1340.921px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1340.946px;'title='10.000 GB\nduration:0.006\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1341.231px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:09:41\nend:2017-03-24 08:09:41'></div><div class='bar' style='background-color:#90BBD7;height:5.354px;width:200.000px;left:960px; top:1341.255px;'title='10.000 GB\nduration:0.147\nstart:2017-03-24 08:09:41\nend:2017-03-24 08:09:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1348.608px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:09:50\nend:2017-03-24 08:09:50'></div><div class='bar' style='background-color:#90BBD7;height:50.172px;width:200.000px;left:960px; top:1348.632px;'title='10.000 GB\nduration:1.043\nstart:2017-03-24 08:09:50\nend:2017-03-24 08:10:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1400.804px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:10:52\nend:2017-03-24 08:10:52'></div><div class='bar' style='background-color:#90BBD7;height:4.378px;width:200.000px;left:960px; top:1400.833px;'title='10.000 GB\nduration:0.128\nstart:2017-03-24 08:10:52\nend:2017-03-24 08:11:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1407.211px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:11:00\nend:2017-03-24 08:11:00'></div><div class='bar' style='background-color:#90BBD7;height:5.175px;width:200.000px;left:960px; top:1407.234px;'title='10.000 GB\nduration:0.144\nstart:2017-03-24 08:11:00\nend:2017-03-24 08:11:09'></div><div class='bar' style='background-color:#90BBD7;height:29.791px;width:180.000px;left:960px; top:1414.409px;'title='9.000 GB\nduration:0.636\nstart:2017-03-24 08:11:09\nend:2017-03-24 08:11:47'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1446.201px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 08:11:47\nend:2017-03-24 08:11:47'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1446.226px;'title='9.000 GB\nduration:0.073\nstart:2017-03-24 08:11:47\nend:2017-03-24 08:11:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1449.855px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 08:11:51\nend:2017-03-24 08:11:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1449.881px;'title='9.000 GB\nduration:0.019\nstart:2017-03-24 08:11:51\nend:2017-03-24 08:11:52'></div><div class='bar' style='background-color:#90BBD7;height:12.364px;width:160.000px;left:960px; top:1450.814px;'title='8.000 GB\nduration:0.287\nstart:2017-03-24 08:11:52\nend:2017-03-24 08:12:10'></div><div class='bar' style='background-color:#90BBD7;height:62.675px;width:140.000px;left:960px; top:1465.178px;'title='7.000 GB\nduration:1.294\nstart:2017-03-24 08:12:10\nend:2017-03-24 08:13:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1529.853px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:13:27\nend:2017-03-24 08:13:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1529.877px;'title='7.000 GB\nduration:0.019\nstart:2017-03-24 08:13:27\nend:2017-03-24 08:13:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1530.829px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:13:28\nend:2017-03-24 08:13:28'></div><div class='bar' style='background-color:#90BBD7;height:8.307px;width:140.000px;left:960px; top:1530.852px;'title='7.000 GB\nduration:0.206\nstart:2017-03-24 08:13:28\nend:2017-03-24 08:13:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1541.159px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:13:41\nend:2017-03-24 08:13:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1541.183px;'title='7.000 GB\nduration:0.011\nstart:2017-03-24 08:13:41\nend:2017-03-24 08:13:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1541.738px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:13:41\nend:2017-03-24 08:13:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1541.761px;'title='7.000 GB\nduration:0.013\nstart:2017-03-24 08:13:41\nend:2017-03-24 08:13:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1542.424px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:13:42\nend:2017-03-24 08:13:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1542.448px;'title='7.000 GB\nduration:0.007\nstart:2017-03-24 08:13:42\nend:2017-03-24 08:13:43'></div><div class='bar' style='background-color:#90BBD7;height:70.792px;width:120.000px;left:960px; top:1542.777px;'title='6.000 GB\nduration:1.456\nstart:2017-03-24 08:13:43\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1615.569px;'title='5.000 GB\nduration:0.000\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1615.594px;'title='6.000 GB\nduration:0.001\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1615.639px;'title='7.000 GB\nduration:0.001\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1615.689px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1615.740px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1615.788px;'title='10.000 GB\nduration:0.007\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:11'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1616.119px;'title='9.000 GB\nduration:0.042\nstart:2017-03-24 08:15:11\nend:2017-03-24 08:15:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1618.234px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 08:15:13\nend:2017-03-24 08:15:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1618.258px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:15:13\nend:2017-03-24 08:15:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1618.306px;'title='10.000 GB\nduration:0.013\nstart:2017-03-24 08:15:13\nend:2017-03-24 08:15:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1618.932px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:15:14\nend:2017-03-24 08:15:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1618.957px;'title='10.000 GB\nduration:0.028\nstart:2017-03-24 08:15:14\nend:2017-03-24 08:15:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1620.376px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:15:16\nend:2017-03-24 08:15:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1620.401px;'title='10.000 GB\nduration:0.033\nstart:2017-03-24 08:15:16\nend:2017-03-24 08:15:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1622.072px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:15:18\nend:2017-03-24 08:15:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1622.095px;'title='10.000 GB\nduration:0.061\nstart:2017-03-24 08:15:18\nend:2017-03-24 08:15:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1625.151px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1625.175px;'title='10.000 GB\nduration:0.003\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1625.319px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:22'></div><div class='bar' style='background-color:#90BBD7;height:6.009px;width:200.000px;left:960px; top:1625.343px;'title='10.000 GB\nduration:0.160\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1633.352px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:15:31\nend:2017-03-24 08:15:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1633.376px;'title='10.000 GB\nduration:0.012\nstart:2017-03-24 08:15:31\nend:2017-03-24 08:15:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1633.997px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:15:32\nend:2017-03-24 08:15:32'></div><div class='bar' style='background-color:#90BBD7;height:44.923px;width:200.000px;left:960px; top:1634.021px;'title='10.000 GB\nduration:0.938\nstart:2017-03-24 08:15:32\nend:2017-03-24 08:16:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1680.944px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:16:28\nend:2017-03-24 08:16:29'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1680.966px;'title='10.000 GB\nduration:0.040\nstart:2017-03-24 08:16:29\nend:2017-03-24 08:16:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1682.967px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:16:31\nend:2017-03-24 08:16:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1682.990px;'title='10.000 GB\nduration:0.008\nstart:2017-03-24 08:16:31\nend:2017-03-24 08:16:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1683.372px;'title='9.000 GB\nduration:0.006\nstart:2017-03-24 08:16:31\nend:2017-03-24 08:16:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1683.647px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 08:16:32\nend:2017-03-24 08:16:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1683.671px;'title='9.000 GB\nduration:0.017\nstart:2017-03-24 08:16:32\nend:2017-03-24 08:16:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1684.509px;'title='8.000 GB\nduration:0.015\nstart:2017-03-24 08:16:33\nend:2017-03-24 08:16:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1685.243px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:16:34\nend:2017-03-24 08:16:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1685.267px;'title='8.000 GB\nduration:0.022\nstart:2017-03-24 08:16:34\nend:2017-03-24 08:16:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1686.344px;'title='7.000 GB\nduration:0.085\nstart:2017-03-24 08:16:35\nend:2017-03-24 08:16:40'></div><div class='bar' style='background-color:#90BBD7;height:34.346px;width:120.000px;left:960px; top:1690.597px;'title='6.000 GB\nduration:0.727\nstart:2017-03-24 08:16:40\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1726.943px;'title='5.000 GB\nduration:0.000\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1726.964px;'title='6.000 GB\nduration:0.001\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1727.009px;'title='7.000 GB\nduration:0.001\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1727.056px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1727.107px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1727.158px;'title='10.000 GB\nduration:0.005\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1727.404px;'title='9.000 GB\nduration:0.051\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1729.936px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 08:17:27\nend:2017-03-24 08:17:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1729.959px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:17:27\nend:2017-03-24 08:17:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1730.007px;'title='10.000 GB\nduration:0.014\nstart:2017-03-24 08:17:27\nend:2017-03-24 08:17:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1730.715px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:17:28\nend:2017-03-24 08:17:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1730.739px;'title='10.000 GB\nduration:0.029\nstart:2017-03-24 08:17:28\nend:2017-03-24 08:17:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1732.197px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:17:30\nend:2017-03-24 08:17:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1732.220px;'title='10.000 GB\nduration:0.039\nstart:2017-03-24 08:17:30\nend:2017-03-24 08:17:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1734.147px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:17:32\nend:2017-03-24 08:17:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1734.170px;'title='10.000 GB\nduration:0.026\nstart:2017-03-24 08:17:32\nend:2017-03-24 08:17:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1735.455px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:17:34\nend:2017-03-24 08:17:34'></div><div class='bar' style='background-color:#90BBD7;height:4.826px;width:200.000px;left:960px; top:1735.478px;'title='10.000 GB\nduration:0.137\nstart:2017-03-24 08:17:34\nend:2017-03-24 08:17:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1742.304px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:17:42\nend:2017-03-24 08:17:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1742.326px;'title='10.000 GB\nduration:0.012\nstart:2017-03-24 08:17:42\nend:2017-03-24 08:17:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1742.937px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:17:43\nend:2017-03-24 08:17:43'></div><div class='bar' style='background-color:#90BBD7;height:52.600px;width:200.000px;left:960px; top:1742.960px;'title='10.000 GB\nduration:1.092\nstart:2017-03-24 08:17:43\nend:2017-03-24 08:18:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1797.560px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:18:48\nend:2017-03-24 08:18:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1797.580px;'title='10.000 GB\nduration:0.026\nstart:2017-03-24 08:18:48\nend:2017-03-24 08:18:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1798.860px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:18:50\nend:2017-03-24 08:18:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1798.882px;'title='10.000 GB\nduration:0.010\nstart:2017-03-24 08:18:50\nend:2017-03-24 08:18:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1799.368px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:18:51\nend:2017-03-24 08:18:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:1799.391px;'title='10.000 GB\nduration:0.003\nstart:2017-03-24 08:18:51\nend:2017-03-24 08:18:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:1799.544px;'title='9.000 GB\nduration:0.015\nstart:2017-03-24 08:18:51\nend:2017-03-24 08:18:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:1800.308px;'title='8.000 GB\nduration:0.011\nstart:2017-03-24 08:18:52\nend:2017-03-24 08:18:52'></div><div class='bar' style='background-color:#90BBD7;height:35.614px;width:140.000px;left:960px; top:1800.865px;'title='7.000 GB\nduration:0.752\nstart:2017-03-24 08:18:52\nend:2017-03-24 08:19:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1838.478px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:19:38\nend:2017-03-24 08:19:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1838.500px;'title='7.000 GB\nduration:0.095\nstart:2017-03-24 08:19:38\nend:2017-03-24 08:19:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1843.241px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:19:43\nend:2017-03-24 08:19:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:1843.264px;'title='7.000 GB\nduration:0.019\nstart:2017-03-24 08:19:43\nend:2017-03-24 08:19:44'></div><div class='bar' style='background-color:#90BBD7;height:33.261px;width:120.000px;left:960px; top:1844.209px;'title='6.000 GB\nduration:0.705\nstart:2017-03-24 08:19:44\nend:2017-03-24 08:20:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1879.470px;'title='5.000 GB\nduration:0.000\nstart:2017-03-24 08:20:27\nend:2017-03-24 08:20:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1879.493px;'title='6.000 GB\nduration:0.018\nstart:2017-03-24 08:20:27\nend:2017-03-24 08:20:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1880.405px;'title='5.000 GB\nduration:0.000\nstart:2017-03-24 08:20:28\nend:2017-03-24 08:20:28'></div><div class='bar' style='background-color:#90BBD7;height:8.094px;width:120.000px;left:960px; top:1880.428px;'title='6.000 GB\nduration:0.202\nstart:2017-03-24 08:20:28\nend:2017-03-24 08:20:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1890.522px;'title='5.000 GB\nduration:0.000\nstart:2017-03-24 08:20:40\nend:2017-03-24 08:20:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1890.545px;'title='6.000 GB\nduration:0.011\nstart:2017-03-24 08:20:40\nend:2017-03-24 08:20:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1891.099px;'title='5.000 GB\nduration:0.001\nstart:2017-03-24 08:20:41\nend:2017-03-24 08:20:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1891.124px;'title='6.000 GB\nduration:0.013\nstart:2017-03-24 08:20:41\nend:2017-03-24 08:20:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1891.796px;'title='5.000 GB\nduration:0.001\nstart:2017-03-24 08:20:42\nend:2017-03-24 08:20:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:1891.823px;'title='6.000 GB\nduration:0.007\nstart:2017-03-24 08:20:42\nend:2017-03-24 08:20:42'></div><div class='bar' style='background-color:#90BBD7;height:35.200px;width:100.000px;left:960px; top:1892.154px;'title='5.000 GB\nduration:0.744\nstart:2017-03-24 08:20:42\nend:2017-03-24 08:21:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:1929.354px;'title='4.000 GB\nduration:0.001\nstart:2017-03-24 08:21:27\nend:2017-03-24 08:21:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1929.383px;'title='5.000 GB\nduration:0.018\nstart:2017-03-24 08:21:27\nend:2017-03-24 08:21:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:1930.296px;'title='4.000 GB\nduration:0.001\nstart:2017-03-24 08:21:28\nend:2017-03-24 08:21:28'></div><div class='bar' style='background-color:#90BBD7;height:9.360px;width:100.000px;left:960px; top:1930.322px;'title='5.000 GB\nduration:0.227\nstart:2017-03-24 08:21:28\nend:2017-03-24 08:21:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:1941.682px;'title='4.000 GB\nduration:0.000\nstart:2017-03-24 08:21:41\nend:2017-03-24 08:21:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1941.705px;'title='5.000 GB\nduration:0.011\nstart:2017-03-24 08:21:41\nend:2017-03-24 08:21:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:1942.259px;'title='4.000 GB\nduration:0.000\nstart:2017-03-24 08:21:42\nend:2017-03-24 08:21:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1942.282px;'title='5.000 GB\nduration:0.012\nstart:2017-03-24 08:21:42\nend:2017-03-24 08:21:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:1942.903px;'title='4.000 GB\nduration:0.000\nstart:2017-03-24 08:21:43\nend:2017-03-24 08:21:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:1942.924px;'title='5.000 GB\nduration:0.007\nstart:2017-03-24 08:21:43\nend:2017-03-24 08:21:43'></div><div class='bar' style='background-color:#90BBD7;height:130.210px;width:80.000px;left:960px; top:1943.253px;'title='4.000 GB\nduration:2.644\nstart:2017-03-24 08:21:43\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:2075.462px;'title='3.000 GB\nduration:0.000\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2075.486px;'title='4.000 GB\nduration:0.001\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2075.534px;'title='5.000 GB\nduration:0.001\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2075.583px;'title='6.000 GB\nduration:0.001\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2075.628px;'title='7.000 GB\nduration:0.001\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2075.676px;'title='8.000 GB\nduration:0.007\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2076.009px;'title='7.000 GB\nduration:0.059\nstart:2017-03-24 08:24:23\nend:2017-03-24 08:24:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2078.958px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2078.981px;'title='7.000 GB\nduration:0.001\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2079.029px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2079.074px;'title='9.000 GB\nduration:0.012\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2079.698px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 08:24:27\nend:2017-03-24 08:24:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2079.720px;'title='9.000 GB\nduration:0.033\nstart:2017-03-24 08:24:27\nend:2017-03-24 08:24:29'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2081.346px;'title='8.000 GB\nduration:0.044\nstart:2017-03-24 08:24:29\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2083.542px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2083.566px;'title='8.000 GB\nduration:0.002\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2083.651px;'title='9.000 GB\nduration:0.002\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2083.736px;'title='10.000 GB\nduration:0.003\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2083.888px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2083.912px;'title='10.000 GB\nduration:0.009\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2084.363px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2084.386px;'title='10.000 GB\nduration:0.003\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2084.538px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2084.562px;'title='10.000 GB\nduration:0.056\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2087.372px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:24:36\nend:2017-03-24 08:24:36'></div><div class='bar' style='background-color:#90BBD7;height:3.105px;width:200.000px;left:960px; top:2087.395px;'title='10.000 GB\nduration:0.102\nstart:2017-03-24 08:24:36\nend:2017-03-24 08:24:42'></div><div class='bar' style='background-color:#90BBD7;height:35.152px;width:180.000px;left:960px; top:2092.500px;'title='9.000 GB\nduration:0.743\nstart:2017-03-24 08:24:42\nend:2017-03-24 08:25:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2129.652px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 08:25:27\nend:2017-03-24 08:25:27'></div><div class='bar' style='background-color:#90BBD7;height:3.360px;width:180.000px;left:960px; top:2129.676px;'title='9.000 GB\nduration:0.107\nstart:2017-03-24 08:25:27\nend:2017-03-24 08:25:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2135.036px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 08:25:33\nend:2017-03-24 08:25:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2135.060px;'title='9.000 GB\nduration:0.019\nstart:2017-03-24 08:25:33\nend:2017-03-24 08:25:35'></div><div class='bar' style='background-color:#90BBD7;height:32.575px;width:160.000px;left:960px; top:2135.994px;'title='8.000 GB\nduration:0.691\nstart:2017-03-24 08:25:35\nend:2017-03-24 08:26:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2170.569px;'title='7.000 GB\nduration:0.020\nstart:2017-03-24 08:26:16\nend:2017-03-24 08:26:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2171.544px;'title='6.000 GB\nduration:0.030\nstart:2017-03-24 08:26:17\nend:2017-03-24 08:26:19'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2173.020px;'title='5.000 GB\nduration:0.063\nstart:2017-03-24 08:26:19\nend:2017-03-24 08:26:23'></div><div class='bar' style='background-color:#90BBD7;height:20.406px;width:80.000px;left:960px; top:2176.177px;'title='4.000 GB\nduration:0.448\nstart:2017-03-24 08:26:23\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:2198.583px;'title='3.000 GB\nduration:0.000\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2198.606px;'title='4.000 GB\nduration:0.001\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2198.651px;'title='5.000 GB\nduration:0.001\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2198.698px;'title='6.000 GB\nduration:0.001\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2198.749px;'title='7.000 GB\nduration:0.001\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2198.799px;'title='8.000 GB\nduration:0.008\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2199.183px;'title='7.000 GB\nduration:0.060\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2202.181px;'title='6.000 GB\nduration:0.001\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:26:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2202.212px;'title='7.000 GB\nduration:0.001\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:26:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2202.264px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:26:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2202.320px;'title='9.000 GB\nduration:0.014\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:26:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2203.043px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 08:26:55\nend:2017-03-24 08:26:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2203.073px;'title='9.000 GB\nduration:0.043\nstart:2017-03-24 08:26:55\nend:2017-03-24 08:26:58'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2205.216px;'title='8.000 GB\nduration:0.041\nstart:2017-03-24 08:26:58\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2207.259px;'title='7.000 GB\nduration:0.004\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2207.452px;'title='6.000 GB\nduration:0.001\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2207.480px;'title='7.000 GB\nduration:0.002\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2207.564px;'title='8.000 GB\nduration:0.002\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2207.647px;'title='9.000 GB\nduration:0.002\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2207.728px;'title='10.000 GB\nduration:0.011\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2208.265px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2208.287px;'title='10.000 GB\nduration:0.064\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:05'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2211.480px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:27:05\nend:2017-03-24 08:27:05'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2211.503px;'title='10.000 GB\nduration:0.013\nstart:2017-03-24 08:27:05\nend:2017-03-24 08:27:06'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2212.128px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:27:06\nend:2017-03-24 08:27:06'></div><div class='bar' style='background-color:#90BBD7;height:3.955px;width:200.000px;left:960px; top:2212.150px;'title='10.000 GB\nduration:0.119\nstart:2017-03-24 08:27:06\nend:2017-03-24 08:27:13'></div><div class='bar' style='background-color:#90BBD7;height:34.898px;width:180.000px;left:960px; top:2218.105px;'title='9.000 GB\nduration:0.738\nstart:2017-03-24 08:27:13\nend:2017-03-24 08:27:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2255.003px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 08:27:57\nend:2017-03-24 08:27:57'></div><div class='bar' style='background-color:#90BBD7;height:3.823px;width:180.000px;left:960px; top:2255.030px;'title='9.000 GB\nduration:0.116\nstart:2017-03-24 08:27:57\nend:2017-03-24 08:28:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2260.853px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 08:28:04\nend:2017-03-24 08:28:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2260.877px;'title='9.000 GB\nduration:0.019\nstart:2017-03-24 08:28:04\nend:2017-03-24 08:28:06'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2261.833px;'title='8.000 GB\nduration:0.030\nstart:2017-03-24 08:28:06\nend:2017-03-24 08:28:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2263.318px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2263.342px;'title='8.000 GB\nduration:0.001\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2263.390px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2263.437px;'title='10.000 GB\nduration:0.007\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2263.769px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:28:08\nend:2017-03-24 08:28:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2263.791px;'title='10.000 GB\nduration:0.079\nstart:2017-03-24 08:28:08\nend:2017-03-24 08:28:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2267.744px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:28:13\nend:2017-03-24 08:28:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2267.773px;'title='10.000 GB\nduration:0.049\nstart:2017-03-24 08:28:13\nend:2017-03-24 08:28:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2270.202px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:28:16\nend:2017-03-24 08:28:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2270.226px;'title='10.000 GB\nduration:0.039\nstart:2017-03-24 08:28:16\nend:2017-03-24 08:28:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2272.158px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:28:18\nend:2017-03-24 08:28:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2272.178px;'title='10.000 GB\nduration:0.043\nstart:2017-03-24 08:28:18\nend:2017-03-24 08:28:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2274.351px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:28:21\nend:2017-03-24 08:28:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2274.374px;'title='10.000 GB\nduration:0.035\nstart:2017-03-24 08:28:21\nend:2017-03-24 08:28:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2276.134px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:28:23\nend:2017-03-24 08:28:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2276.158px;'title='10.000 GB\nduration:0.025\nstart:2017-03-24 08:28:23\nend:2017-03-24 08:28:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2277.391px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:28:24\nend:2017-03-24 08:28:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2277.419px;'title='10.000 GB\nduration:0.098\nstart:2017-03-24 08:28:24\nend:2017-03-24 08:28:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2282.339px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:28:30\nend:2017-03-24 08:28:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2282.367px;'title='10.000 GB\nduration:0.013\nstart:2017-03-24 08:28:30\nend:2017-03-24 08:28:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2282.996px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:28:31\nend:2017-03-24 08:28:31'></div><div class='bar' style='background-color:#90BBD7;height:9.357px;width:200.000px;left:960px; top:2283.019px;'title='10.000 GB\nduration:0.227\nstart:2017-03-24 08:28:31\nend:2017-03-24 08:28:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2294.376px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:28:45\nend:2017-03-24 08:28:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2294.399px;'title='10.000 GB\nduration:0.059\nstart:2017-03-24 08:28:45\nend:2017-03-24 08:28:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2297.361px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:28:48\nend:2017-03-24 08:28:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2297.383px;'title='10.000 GB\nduration:0.046\nstart:2017-03-24 08:28:48\nend:2017-03-24 08:28:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2299.685px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:28:51\nend:2017-03-24 08:28:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2299.711px;'title='10.000 GB\nduration:0.017\nstart:2017-03-24 08:28:51\nend:2017-03-24 08:28:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2300.546px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:28:52\nend:2017-03-24 08:28:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2300.580px;'title='10.000 GB\nduration:0.045\nstart:2017-03-24 08:28:52\nend:2017-03-24 08:28:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2302.841px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:28:55\nend:2017-03-24 08:28:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2302.874px;'title='10.000 GB\nduration:0.016\nstart:2017-03-24 08:28:55\nend:2017-03-24 08:28:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2303.684px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:28:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2303.756px;'title='8.000 GB\nduration:0.000\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:28:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2303.779px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:28:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2303.826px;'title='10.000 GB\nduration:0.093\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:29:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2308.498px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:29:02\nend:2017-03-24 08:29:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2308.524px;'title='10.000 GB\nduration:0.095\nstart:2017-03-24 08:29:02\nend:2017-03-24 08:29:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2313.271px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:29:07\nend:2017-03-24 08:29:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2313.294px;'title='10.000 GB\nduration:0.007\nstart:2017-03-24 08:29:07\nend:2017-03-24 08:29:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2313.639px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:29:08\nend:2017-03-24 08:29:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2313.662px;'title='10.000 GB\nduration:0.071\nstart:2017-03-24 08:29:08\nend:2017-03-24 08:29:12'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2317.191px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:29:12\nend:2017-03-24 08:29:12'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2317.214px;'title='10.000 GB\nduration:0.085\nstart:2017-03-24 08:29:12\nend:2017-03-24 08:29:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2321.486px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:29:17\nend:2017-03-24 08:29:17'></div><div class='bar' style='background-color:#90BBD7;height:7.191px;width:200.000px;left:960px; top:2321.512px;'title='10.000 GB\nduration:0.184\nstart:2017-03-24 08:29:17\nend:2017-03-24 08:29:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2330.703px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:29:28\nend:2017-03-24 08:29:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2330.724px;'title='10.000 GB\nduration:0.016\nstart:2017-03-24 08:29:28\nend:2017-03-24 08:29:29'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2331.543px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:29:29\nend:2017-03-24 08:29:29'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2331.565px;'title='10.000 GB\nduration:0.013\nstart:2017-03-24 08:29:29\nend:2017-03-24 08:29:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2332.229px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:29:30\nend:2017-03-24 08:29:30'></div><div class='bar' style='background-color:#90BBD7;height:3.033px;width:200.000px;left:960px; top:2332.251px;'title='10.000 GB\nduration:0.101\nstart:2017-03-24 08:29:30\nend:2017-03-24 08:29:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2337.283px;'title='9.000 GB\nduration:0.001\nstart:2017-03-24 08:29:36\nend:2017-03-24 08:29:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2337.312px;'title='10.000 GB\nduration:0.013\nstart:2017-03-24 08:29:36\nend:2017-03-24 08:29:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2337.965px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:29:37\nend:2017-03-24 08:29:37'></div><div class='bar' style='background-color:#90BBD7;height:19.986px;width:200.000px;left:960px; top:2337.986px;'title='10.000 GB\nduration:0.440\nstart:2017-03-24 08:29:37\nend:2017-03-24 08:30:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2359.972px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:30:03\nend:2017-03-24 08:30:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2359.994px;'title='10.000 GB\nduration:0.059\nstart:2017-03-24 08:30:03\nend:2017-03-24 08:30:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2362.963px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:30:07\nend:2017-03-24 08:30:07'></div><div class='bar' style='background-color:#90BBD7;height:4.471px;width:200.000px;left:960px; top:2362.987px;'title='10.000 GB\nduration:0.129\nstart:2017-03-24 08:30:07\nend:2017-03-24 08:30:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2369.459px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:30:15\nend:2017-03-24 08:30:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2369.482px;'title='10.000 GB\nduration:0.098\nstart:2017-03-24 08:30:15\nend:2017-03-24 08:30:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2374.385px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:30:21\nend:2017-03-24 08:30:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2374.409px;'title='10.000 GB\nduration:0.041\nstart:2017-03-24 08:30:21\nend:2017-03-24 08:30:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2376.462px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:30:23\nend:2017-03-24 08:30:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2376.484px;'title='10.000 GB\nduration:0.019\nstart:2017-03-24 08:30:23\nend:2017-03-24 08:30:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2377.456px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:30:24\nend:2017-03-24 08:30:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2377.478px;'title='10.000 GB\nduration:0.038\nstart:2017-03-24 08:30:24\nend:2017-03-24 08:30:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2379.394px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:30:27\nend:2017-03-24 08:30:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2379.416px;'title='10.000 GB\nduration:0.019\nstart:2017-03-24 08:30:27\nend:2017-03-24 08:30:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2380.374px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:30:28\nend:2017-03-24 08:30:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2380.396px;'title='10.000 GB\nduration:0.073\nstart:2017-03-24 08:30:28\nend:2017-03-24 08:30:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:960px; top:2384.057px;'title='9.000 GB\nduration:0.000\nstart:2017-03-24 08:30:32\nend:2017-03-24 08:30:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:960px; top:2384.080px;'title='10.000 GB\nduration:0.020\nstart:2017-03-24 08:30:32\nend:2017-03-24 08:30:33'></div><div class='bar' style='background-color:#90BBD7;height:3.484px;width:180.000px;left:960px; top:2385.056px;'title='9.000 GB\nduration:0.110\nstart:2017-03-24 08:30:33\nend:2017-03-24 08:30:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2390.540px;'title='8.000 GB\nduration:0.061\nstart:2017-03-24 08:30:40\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2393.568px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2393.590px;'title='8.000 GB\nduration:0.011\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2394.144px;'title='7.000 GB\nduration:0.000\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:960px; top:2394.166px;'title='8.000 GB\nduration:0.007\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2394.497px;'title='7.000 GB\nduration:0.008\nstart:2017-03-24 08:30:45\nend:2017-03-24 08:30:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2394.883px;'title='6.000 GB\nduration:0.000\nstart:2017-03-24 08:30:45\nend:2017-03-24 08:30:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:960px; top:2394.905px;'title='7.000 GB\nduration:0.007\nstart:2017-03-24 08:30:45\nend:2017-03-24 08:30:46'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:960px; top:2395.236px;'title='6.000 GB\nduration:0.089\nstart:2017-03-24 08:30:46\nend:2017-03-24 08:30:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2399.691px;'title='5.000 GB\nduration:0.092\nstart:2017-03-24 08:30:51\nend:2017-03-24 08:30:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2404.278px;'title='4.000 GB\nduration:0.000\nstart:2017-03-24 08:30:56\nend:2017-03-24 08:30:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2404.300px;'title='5.000 GB\nduration:0.019\nstart:2017-03-24 08:30:57\nend:2017-03-24 08:30:58'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2405.252px;'title='4.000 GB\nduration:0.000\nstart:2017-03-24 08:30:58\nend:2017-03-24 08:30:58'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:960px; top:2405.274px;'title='5.000 GB\nduration:0.035\nstart:2017-03-24 08:30:58\nend:2017-03-24 08:31:00'></div><div class='bar' style='background-color:#90BBD7;height:10.271px;width:80.000px;left:960px; top:2407.009px;'title='4.000 GB\nduration:0.245\nstart:2017-03-24 08:31:00\nend:2017-03-24 08:31:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:2419.280px;'title='3.000 GB\nduration:0.000\nstart:2017-03-24 08:31:14\nend:2017-03-24 08:31:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2419.303px;'title='4.000 GB\nduration:0.011\nstart:2017-03-24 08:31:15\nend:2017-03-24 08:31:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:2419.854px;'title='3.000 GB\nduration:0.000\nstart:2017-03-24 08:31:15\nend:2017-03-24 08:31:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2419.875px;'title='4.000 GB\nduration:0.013\nstart:2017-03-24 08:31:15\nend:2017-03-24 08:31:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:2420.538px;'title='3.000 GB\nduration:0.000\nstart:2017-03-24 08:31:16\nend:2017-03-24 08:31:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:960px; top:2420.560px;'title='4.000 GB\nduration:0.007\nstart:2017-03-24 08:31:16\nend:2017-03-24 08:31:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:960px; top:2420.890px;'title='3.000 GB\nduration:0.092\nstart:2017-03-24 08:31:16\nend:2017-03-24 08:31:22'></div><div class='bar' style='background-color:#90BBD7;height:47.085px;width:40.000px;left:960px; top:2425.512px;'title='2.000 GB\nduration:0.982\nstart:2017-03-24 08:31:22\nend:2017-03-24 08:32:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2474.597px;'title='1.000 GB\nduration:0.000\nstart:2017-03-24 08:32:21\nend:2017-03-24 08:32:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:2474.619px;'title='2.000 GB\nduration:0.024\nstart:2017-03-24 08:32:21\nend:2017-03-24 08:32:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2475.825px;'title='1.000 GB\nduration:0.000\nstart:2017-03-24 08:32:22\nend:2017-03-24 08:32:22'></div><div class='bar' style='background-color:#90BBD7;height:10.190px;width:40.000px;left:960px; top:2475.846px;'title='2.000 GB\nduration:0.244\nstart:2017-03-24 08:32:22\nend:2017-03-24 08:32:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2488.036px;'title='1.000 GB\nduration:0.000\nstart:2017-03-24 08:32:37\nend:2017-03-24 08:32:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:2488.058px;'title='2.000 GB\nduration:0.011\nstart:2017-03-24 08:32:37\nend:2017-03-24 08:32:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2488.613px;'title='1.000 GB\nduration:0.001\nstart:2017-03-24 08:32:38\nend:2017-03-24 08:32:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:2488.640px;'title='2.000 GB\nduration:0.014\nstart:2017-03-24 08:32:38\nend:2017-03-24 08:32:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2489.348px;'title='1.000 GB\nduration:0.000\nstart:2017-03-24 08:32:39\nend:2017-03-24 08:32:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:960px; top:2489.370px;'title='2.000 GB\nduration:0.007\nstart:2017-03-24 08:32:39\nend:2017-03-24 08:32:39'></div><div class='bar' style='background-color:#90BBD7;height:68.544px;width:20.000px;left:960px; top:2489.701px;'title='1.000 GB\nduration:1.411\nstart:2017-03-24 08:32:39\nend:2017-03-24 08:34:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2560.245px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:34:04\nend:2017-03-24 08:34:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2560.264px;'title='1.000 GB\nduration:0.019\nstart:2017-03-24 08:34:04\nend:2017-03-24 08:34:05'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2561.217px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:34:05\nend:2017-03-24 08:34:05'></div><div class='bar' style='background-color:#90BBD7;height:10.606px;width:20.000px;left:960px; top:2561.238px;'title='1.000 GB\nduration:0.252\nstart:2017-03-24 08:34:05\nend:2017-03-24 08:34:20'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2573.845px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:34:20\nend:2017-03-24 08:34:20'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2573.866px;'title='1.000 GB\nduration:0.011\nstart:2017-03-24 08:34:20\nend:2017-03-24 08:34:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2574.416px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:34:21\nend:2017-03-24 08:34:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2574.436px;'title='1.000 GB\nduration:0.013\nstart:2017-03-24 08:34:21\nend:2017-03-24 08:34:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2575.096px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:34:21\nend:2017-03-24 08:34:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:960px; top:2575.117px;'title='1.000 GB\nduration:0.007\nstart:2017-03-24 08:34:21\nend:2017-03-24 08:34:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:960px; top:2575.448px;'title='0.000 GB\nduration:0.000\nstart:2017-03-24 08:34:22\nend:2017-03-24 08:34:22'></div><p class='time' style='top:198px;left:960px;'>Memory</p><div class='bar' style='background-color:#03969D;height:2353.448px;width:0.000px;left:960px; top:220.000px;'title='0.000 GB\nduration:47.109\nstart:2017-03-24 07:47:15\nend:2017-03-24 08:34:22'></div><p class='time' style='top:198px;left:420px;'>Threads</p><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:220.000px;'title='1 threads\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:220.021px;'title='2 threads\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:220.035px;'title='3 threads\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:220.051px;'title='4 threads\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:220.067px;'title='5 threads\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:220.084px;'title='6 threads\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:220.102px;'title='7 threads\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:220.123px;'title='8 threads\nduration:0.000\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:220.138px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:16\nend:2017-03-24 07:47:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:220.155px;'title='10 threads\nduration:0.016\nstart:2017-03-24 07:47:16\nend:2017-03-24 07:47:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:220.942px;'title='9 threads\nduration:0.003\nstart:2017-03-24 07:47:16\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:221.091px;'title='8 threads\nduration:0.003\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:221.221px;'title='7 threads\nduration:0.001\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:221.251px;'title='8 threads\nduration:0.000\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:221.264px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:221.277px;'title='10 threads\nduration:0.003\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:221.409px;'title='9 threads\nduration:0.003\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:221.547px;'title='8 threads\nduration:0.001\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:221.574px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:221.587px;'title='10 threads\nduration:0.008\nstart:2017-03-24 07:47:17\nend:2017-03-24 07:47:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:221.977px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:18\nend:2017-03-24 07:47:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:222.001px;'title='10 threads\nduration:0.008\nstart:2017-03-24 07:47:18\nend:2017-03-24 07:47:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:222.387px;'title='9 threads\nduration:0.005\nstart:2017-03-24 07:47:18\nend:2017-03-24 07:47:19'></div><div class='bar' style='background-color:#90BBD7;height:3.267px;width:160.000px;left:420px; top:222.627px;'title='8 threads\nduration:0.105\nstart:2017-03-24 07:47:19\nend:2017-03-24 07:47:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:227.894px;'title='7 threads\nduration:0.000\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:227.918px;'title='8 threads\nduration:0.001\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:227.966px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:228.010px;'title='10 threads\nduration:0.037\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:229.852px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:27\nend:2017-03-24 07:47:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:229.877px;'title='10 threads\nduration:0.059\nstart:2017-03-24 07:47:27\nend:2017-03-24 07:47:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:232.842px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:47:31\nend:2017-03-24 07:47:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:232.868px;'title='10 threads\nduration:0.003\nstart:2017-03-24 07:47:31\nend:2017-03-24 07:47:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:233.010px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:47:31\nend:2017-03-24 07:47:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:233.036px;'title='10 threads\nduration:0.035\nstart:2017-03-24 07:47:31\nend:2017-03-24 07:47:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:234.808px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:33\nend:2017-03-24 07:47:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:234.831px;'title='10 threads\nduration:0.008\nstart:2017-03-24 07:47:33\nend:2017-03-24 07:47:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:235.236px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:34\nend:2017-03-24 07:47:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:235.259px;'title='10 threads\nduration:0.038\nstart:2017-03-24 07:47:34\nend:2017-03-24 07:47:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:237.141px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:47:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:237.165px;'title='10 threads\nduration:0.005\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:47:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:237.408px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:47:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:237.430px;'title='10 threads\nduration:0.025\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:47:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:238.677px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:38\nend:2017-03-24 07:47:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:238.701px;'title='10 threads\nduration:0.050\nstart:2017-03-24 07:47:38\nend:2017-03-24 07:47:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:241.208px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:47:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:241.234px;'title='10 threads\nduration:0.007\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:47:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:241.605px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:47:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:241.628px;'title='10 threads\nduration:0.012\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:47:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:242.233px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:42\nend:2017-03-24 07:47:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:242.256px;'title='10 threads\nduration:0.018\nstart:2017-03-24 07:47:42\nend:2017-03-24 07:47:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:243.165px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:243.190px;'title='10 threads\nduration:0.003\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:243.321px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:243.346px;'title='10 threads\nduration:0.007\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:243.673px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:44\nend:2017-03-24 07:47:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:243.698px;'title='10 threads\nduration:0.019\nstart:2017-03-24 07:47:44\nend:2017-03-24 07:47:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:244.656px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:45\nend:2017-03-24 07:47:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:244.680px;'title='10 threads\nduration:0.045\nstart:2017-03-24 07:47:45\nend:2017-03-24 07:47:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:246.938px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:47:48\nend:2017-03-24 07:47:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:246.963px;'title='10 threads\nduration:0.036\nstart:2017-03-24 07:47:48\nend:2017-03-24 07:47:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:248.755px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:50\nend:2017-03-24 07:47:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:248.779px;'title='10 threads\nduration:0.031\nstart:2017-03-24 07:47:50\nend:2017-03-24 07:47:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:250.352px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:52\nend:2017-03-24 07:47:52'></div><div class='bar' style='background-color:#90BBD7;height:3.784px;width:200.000px;left:420px; top:250.374px;'title='10 threads\nduration:0.116\nstart:2017-03-24 07:47:52\nend:2017-03-24 07:47:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:256.158px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:47:59\nend:2017-03-24 07:47:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:256.180px;'title='10 threads\nduration:0.075\nstart:2017-03-24 07:47:59\nend:2017-03-24 07:48:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:259.937px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:48:03\nend:2017-03-24 07:48:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:259.962px;'title='10 threads\nduration:0.036\nstart:2017-03-24 07:48:03\nend:2017-03-24 07:48:05'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:261.780px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:48:05\nend:2017-03-24 07:48:06'></div><div class='bar' style='background-color:#90BBD7;height:36.892px;width:200.000px;left:420px; top:261.805px;'title='10 threads\nduration:0.778\nstart:2017-03-24 07:48:06\nend:2017-03-24 07:48:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:300.697px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:48:52\nend:2017-03-24 07:48:52'></div><div class='bar' style='background-color:#90BBD7;height:4.637px;width:200.000px;left:420px; top:300.723px;'title='10 threads\nduration:0.133\nstart:2017-03-24 07:48:52\nend:2017-03-24 07:49:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:307.360px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:49:00\nend:2017-03-24 07:49:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:307.385px;'title='10 threads\nduration:0.008\nstart:2017-03-24 07:49:00\nend:2017-03-24 07:49:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:307.783px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:49:01\nend:2017-03-24 07:49:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:307.808px;'title='10 threads\nduration:0.054\nstart:2017-03-24 07:49:01\nend:2017-03-24 07:49:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:310.532px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:04\nend:2017-03-24 07:49:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:310.561px;'title='10 threads\nduration:0.008\nstart:2017-03-24 07:49:04\nend:2017-03-24 07:49:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:310.956px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:04\nend:2017-03-24 07:49:05'></div><div class='bar' style='background-color:#90BBD7;height:7.488px;width:200.000px;left:420px; top:310.984px;'title='10 threads\nduration:0.190\nstart:2017-03-24 07:49:05\nend:2017-03-24 07:49:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:320.472px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:16\nend:2017-03-24 07:49:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:320.499px;'title='10 threads\nduration:0.008\nstart:2017-03-24 07:49:16\nend:2017-03-24 07:49:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:320.897px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:16\nend:2017-03-24 07:49:16'></div><div class='bar' style='background-color:#90BBD7;height:3.748px;width:200.000px;left:420px; top:320.923px;'title='10 threads\nduration:0.115\nstart:2017-03-24 07:49:16\nend:2017-03-24 07:49:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:326.671px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:326.711px;'title='10 threads\nduration:0.010\nstart:2017-03-24 07:49:23\nend:2017-03-24 07:49:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:327.208px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:24\nend:2017-03-24 07:49:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:327.246px;'title='10 threads\nduration:0.099\nstart:2017-03-24 07:49:24\nend:2017-03-24 07:49:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:332.206px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:30\nend:2017-03-24 07:49:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:332.244px;'title='10 threads\nduration:0.098\nstart:2017-03-24 07:49:30\nend:2017-03-24 07:49:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:337.158px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:36\nend:2017-03-24 07:49:36'></div><div class='bar' style='background-color:#90BBD7;height:4.942px;width:200.000px;left:420px; top:337.197px;'title='10 threads\nduration:0.139\nstart:2017-03-24 07:49:36\nend:2017-03-24 07:49:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:344.139px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:44\nend:2017-03-24 07:49:44'></div><div class='bar' style='background-color:#90BBD7;height:3.168px;width:200.000px;left:420px; top:344.180px;'title='10 threads\nduration:0.103\nstart:2017-03-24 07:49:44\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:349.348px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:349.387px;'title='10 threads\nduration:0.070\nstart:2017-03-24 07:49:51\nend:2017-03-24 07:49:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:352.863px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:49:55\nend:2017-03-24 07:49:55'></div><div class='bar' style='background-color:#90BBD7;height:4.219px;width:200.000px;left:420px; top:352.903px;'title='10 threads\nduration:0.124\nstart:2017-03-24 07:49:55\nend:2017-03-24 07:50:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:359.122px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:50:02\nend:2017-03-24 07:50:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:359.161px;'title='10 threads\nduration:0.091\nstart:2017-03-24 07:50:02\nend:2017-03-24 07:50:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:363.703px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:50:08\nend:2017-03-24 07:50:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:363.744px;'title='10 threads\nduration:0.035\nstart:2017-03-24 07:50:08\nend:2017-03-24 07:50:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:365.507px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:50:10\nend:2017-03-24 07:50:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:365.547px;'title='10 threads\nduration:0.009\nstart:2017-03-24 07:50:10\nend:2017-03-24 07:50:11'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:366.009px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:50:11\nend:2017-03-24 07:50:11'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:366.034px;'title='10 threads\nduration:0.051\nstart:2017-03-24 07:50:11\nend:2017-03-24 07:50:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:368.586px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:368.611px;'title='10 threads\nduration:0.008\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:369.010px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:369.036px;'title='10 threads\nduration:0.011\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:369.577px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:50:15\nend:2017-03-24 07:50:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:369.601px;'title='10 threads\nduration:0.064\nstart:2017-03-24 07:50:15\nend:2017-03-24 07:50:19'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:372.822px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:50:19\nend:2017-03-24 07:50:19'></div><div class='bar' style='background-color:#90BBD7;height:3.151px;width:200.000px;left:420px; top:372.896px;'title='10 threads\nduration:0.103\nstart:2017-03-24 07:50:19\nend:2017-03-24 07:50:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:378.047px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:50:25\nend:2017-03-24 07:50:25'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:378.074px;'title='10 threads\nduration:0.008\nstart:2017-03-24 07:50:25\nend:2017-03-24 07:50:26'></div><div class='bar' style='background-color:#90BBD7;height:96.506px;width:180.000px;left:420px; top:378.468px;'title='9 threads\nduration:1.970\nstart:2017-03-24 07:50:26\nend:2017-03-24 07:52:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:476.974px;'title='8 threads\nduration:0.000\nstart:2017-03-24 07:52:24\nend:2017-03-24 07:52:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:476.998px;'title='9 threads\nduration:0.008\nstart:2017-03-24 07:52:24\nend:2017-03-24 07:52:24'></div><div class='bar' style='background-color:#90BBD7;height:28.015px;width:160.000px;left:420px; top:477.393px;'title='8 threads\nduration:0.600\nstart:2017-03-24 07:52:24\nend:2017-03-24 07:53:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:507.408px;'title='7 threads\nduration:0.001\nstart:2017-03-24 07:53:00\nend:2017-03-24 07:53:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:507.434px;'title='8 threads\nduration:0.029\nstart:2017-03-24 07:53:00\nend:2017-03-24 07:53:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:508.896px;'title='7 threads\nduration:0.000\nstart:2017-03-24 07:53:02\nend:2017-03-24 07:53:02'></div><div class='bar' style='background-color:#90BBD7;height:275.136px;width:160.000px;left:420px; top:508.921px;'title='8 threads\nduration:5.543\nstart:2017-03-24 07:53:02\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:786.057px;'title='7 threads\nduration:0.001\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:786.083px;'title='8 threads\nduration:0.001\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:786.134px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:786.185px;'title='10 threads\nduration:0.005\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:786.434px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:786.460px;'title='10 threads\nduration:0.029\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:787.925px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:58:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:787.952px;'title='10 threads\nduration:0.008\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:58:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:788.373px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:58:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:788.399px;'title='10 threads\nduration:0.004\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:58:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:788.610px;'title='9 threads\nduration:0.001\nstart:2017-03-24 07:58:38\nend:2017-03-24 07:58:38'></div><div class='bar' style='background-color:#90BBD7;height:5.366px;width:200.000px;left:420px; top:788.636px;'title='10 threads\nduration:0.147\nstart:2017-03-24 07:58:38\nend:2017-03-24 07:58:47'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:796.002px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:58:47\nend:2017-03-24 07:58:47'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:796.027px;'title='10 threads\nduration:0.044\nstart:2017-03-24 07:58:47\nend:2017-03-24 07:58:49'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:798.245px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:58:49\nend:2017-03-24 07:58:49'></div><div class='bar' style='background-color:#90BBD7;height:52.120px;width:200.000px;left:420px; top:798.269px;'title='10 threads\nduration:1.082\nstart:2017-03-24 07:58:49\nend:2017-03-24 07:59:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:852.389px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:59:54\nend:2017-03-24 07:59:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:852.413px;'title='10 threads\nduration:0.011\nstart:2017-03-24 07:59:54\nend:2017-03-24 07:59:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:852.948px;'title='9 threads\nduration:0.000\nstart:2017-03-24 07:59:55\nend:2017-03-24 07:59:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:852.971px;'title='10 threads\nduration:0.015\nstart:2017-03-24 07:59:55\nend:2017-03-24 07:59:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:853.716px;'title='9 threads\nduration:0.051\nstart:2017-03-24 07:59:56\nend:2017-03-24 07:59:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:856.248px;'title='8 threads\nduration:0.001\nstart:2017-03-24 07:59:59\nend:2017-03-24 07:59:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:856.299px;'title='9 threads\nduration:0.002\nstart:2017-03-24 07:59:59\nend:2017-03-24 07:59:59'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:856.381px;'title='10 threads\nduration:0.012\nstart:2017-03-24 07:59:59\nend:2017-03-24 08:00:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:857.002px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:00:00\nend:2017-03-24 08:00:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:857.027px;'title='10 threads\nduration:0.016\nstart:2017-03-24 08:00:00\nend:2017-03-24 08:00:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:857.815px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:00:01\nend:2017-03-24 08:00:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:857.837px;'title='10 threads\nduration:0.013\nstart:2017-03-24 08:00:01\nend:2017-03-24 08:00:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:858.490px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:00:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:858.513px;'title='10 threads\nduration:0.014\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:00:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:859.227px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:00:02'></div><div class='bar' style='background-color:#90BBD7;height:39.096px;width:200.000px;left:420px; top:859.251px;'title='10 threads\nduration:0.822\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:00:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:900.347px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:00:52\nend:2017-03-24 08:00:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:900.369px;'title='10 threads\nduration:0.047\nstart:2017-03-24 08:00:52\nend:2017-03-24 08:00:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:902.713px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:00:55\nend:2017-03-24 08:00:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:902.736px;'title='10 threads\nduration:0.019\nstart:2017-03-24 08:00:55\nend:2017-03-24 08:00:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:903.668px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:00:56\nend:2017-03-24 08:00:56'></div><div class='bar' style='background-color:#90BBD7;height:5.425px;width:200.000px;left:420px; top:903.691px;'title='10 threads\nduration:0.148\nstart:2017-03-24 08:00:56\nend:2017-03-24 08:01:05'></div><div class='bar' style='background-color:#90BBD7;height:12.046px;width:180.000px;left:420px; top:911.116px;'title='9 threads\nduration:0.281\nstart:2017-03-24 08:01:05\nend:2017-03-24 08:01:22'></div><div class='bar' style='background-color:#90BBD7;height:42.268px;width:160.000px;left:420px; top:925.162px;'title='8 threads\nduration:0.885\nstart:2017-03-24 08:01:22\nend:2017-03-24 08:02:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:969.430px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:02:15\nend:2017-03-24 08:02:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:969.453px;'title='8 threads\nduration:0.050\nstart:2017-03-24 08:02:15\nend:2017-03-24 08:02:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:971.964px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:02:18\nend:2017-03-24 08:02:18'></div><div class='bar' style='background-color:#90BBD7;height:18.952px;width:160.000px;left:420px; top:971.988px;'title='8 threads\nduration:0.419\nstart:2017-03-24 08:02:18\nend:2017-03-24 08:02:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:992.940px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:02:43\nend:2017-03-24 08:02:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:992.963px;'title='8 threads\nduration:0.055\nstart:2017-03-24 08:02:43\nend:2017-03-24 08:02:46'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:995.724px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:02:46\nend:2017-03-24 08:02:46'></div><div class='bar' style='background-color:#90BBD7;height:57.961px;width:160.000px;left:420px; top:995.748px;'title='8 threads\nduration:1.199\nstart:2017-03-24 08:02:46\nend:2017-03-24 08:03:58'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1055.710px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:03:58\nend:2017-03-24 08:03:58'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1055.732px;'title='8 threads\nduration:0.043\nstart:2017-03-24 08:03:58\nend:2017-03-24 08:04:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1057.866px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:04:01\nend:2017-03-24 08:04:01'></div><div class='bar' style='background-color:#90BBD7;height:8.266px;width:160.000px;left:420px; top:1057.888px;'title='8 threads\nduration:0.205\nstart:2017-03-24 08:04:01\nend:2017-03-24 08:04:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1068.154px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:04:13\nend:2017-03-24 08:04:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1068.179px;'title='8 threads\nduration:0.011\nstart:2017-03-24 08:04:13\nend:2017-03-24 08:04:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1068.734px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:04:14\nend:2017-03-24 08:04:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1068.758px;'title='8 threads\nduration:0.014\nstart:2017-03-24 08:04:14\nend:2017-03-24 08:04:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1069.461px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:04:15\nend:2017-03-24 08:04:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1069.485px;'title='8 threads\nduration:0.007\nstart:2017-03-24 08:04:15\nend:2017-03-24 08:04:15'></div><div class='bar' style='background-color:#90BBD7;height:154.155px;width:140.000px;left:420px; top:1069.817px;'title='7 threads\nduration:3.123\nstart:2017-03-24 08:04:15\nend:2017-03-24 08:07:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1225.973px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:07:23\nend:2017-03-24 08:07:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1225.996px;'title='7 threads\nduration:0.065\nstart:2017-03-24 08:07:23\nend:2017-03-24 08:07:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1229.262px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:07:26\nend:2017-03-24 08:07:26'></div><div class='bar' style='background-color:#90BBD7;height:23.702px;width:140.000px;left:420px; top:1229.286px;'title='7 threads\nduration:0.514\nstart:2017-03-24 08:07:26\nend:2017-03-24 08:07:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1254.988px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:07:57\nend:2017-03-24 08:07:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1255.012px;'title='7 threads\nduration:0.071\nstart:2017-03-24 08:07:57\nend:2017-03-24 08:08:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1258.578px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:08:02\nend:2017-03-24 08:08:02'></div><div class='bar' style='background-color:#90BBD7;height:14.458px;width:140.000px;left:420px; top:1258.601px;'title='7 threads\nduration:0.329\nstart:2017-03-24 08:08:02\nend:2017-03-24 08:08:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1275.059px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:08:21\nend:2017-03-24 08:08:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1275.083px;'title='7 threads\nduration:0.078\nstart:2017-03-24 08:08:21\nend:2017-03-24 08:08:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1278.982px;'title='6 threads\nduration:0.001\nstart:2017-03-24 08:08:26\nend:2017-03-24 08:08:26'></div><div class='bar' style='background-color:#90BBD7;height:39.043px;width:140.000px;left:420px; top:1279.009px;'title='7 threads\nduration:0.821\nstart:2017-03-24 08:08:26\nend:2017-03-24 08:09:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1320.052px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:09:15\nend:2017-03-24 08:09:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1320.076px;'title='7 threads\nduration:0.071\nstart:2017-03-24 08:09:15\nend:2017-03-24 08:09:20'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1323.641px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:09:20\nend:2017-03-24 08:09:20'></div><div class='bar' style='background-color:#90BBD7;height:6.894px;width:140.000px;left:420px; top:1323.665px;'title='7 threads\nduration:0.178\nstart:2017-03-24 08:09:20\nend:2017-03-24 08:09:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1332.559px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:09:30\nend:2017-03-24 08:09:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1332.583px;'title='7 threads\nduration:0.001\nstart:2017-03-24 08:09:30\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1332.630px;'title='8 threads\nduration:0.001\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1332.678px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1332.725px;'title='10 threads\nduration:0.007\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1333.052px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1333.076px;'title='10 threads\nduration:0.044\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1335.253px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1335.275px;'title='10 threads\nduration:0.007\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1335.603px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1335.627px;'title='10 threads\nduration:0.017\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1336.458px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:09:35\nend:2017-03-24 08:09:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1336.482px;'title='10 threads\nduration:0.033\nstart:2017-03-24 08:09:35\nend:2017-03-24 08:09:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1338.113px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:09:37\nend:2017-03-24 08:09:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1338.137px;'title='10 threads\nduration:0.037\nstart:2017-03-24 08:09:37\nend:2017-03-24 08:09:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1339.970px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:09:39\nend:2017-03-24 08:09:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1339.994px;'title='10 threads\nduration:0.012\nstart:2017-03-24 08:09:39\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1340.617px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1340.641px;'title='10 threads\nduration:0.006\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1340.921px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1340.946px;'title='10 threads\nduration:0.006\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1341.231px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:09:41\nend:2017-03-24 08:09:41'></div><div class='bar' style='background-color:#90BBD7;height:5.354px;width:200.000px;left:420px; top:1341.255px;'title='10 threads\nduration:0.147\nstart:2017-03-24 08:09:41\nend:2017-03-24 08:09:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1348.608px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:09:50\nend:2017-03-24 08:09:50'></div><div class='bar' style='background-color:#90BBD7;height:50.172px;width:200.000px;left:420px; top:1348.632px;'title='10 threads\nduration:1.043\nstart:2017-03-24 08:09:50\nend:2017-03-24 08:10:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1400.804px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:10:52\nend:2017-03-24 08:10:52'></div><div class='bar' style='background-color:#90BBD7;height:4.378px;width:200.000px;left:420px; top:1400.833px;'title='10 threads\nduration:0.128\nstart:2017-03-24 08:10:52\nend:2017-03-24 08:11:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1407.211px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:11:00\nend:2017-03-24 08:11:00'></div><div class='bar' style='background-color:#90BBD7;height:5.175px;width:200.000px;left:420px; top:1407.234px;'title='10 threads\nduration:0.144\nstart:2017-03-24 08:11:00\nend:2017-03-24 08:11:09'></div><div class='bar' style='background-color:#90BBD7;height:29.791px;width:180.000px;left:420px; top:1414.409px;'title='9 threads\nduration:0.636\nstart:2017-03-24 08:11:09\nend:2017-03-24 08:11:47'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1446.201px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:11:47\nend:2017-03-24 08:11:47'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1446.226px;'title='9 threads\nduration:0.073\nstart:2017-03-24 08:11:47\nend:2017-03-24 08:11:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1449.855px;'title='8 threads\nduration:0.001\nstart:2017-03-24 08:11:51\nend:2017-03-24 08:11:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1449.881px;'title='9 threads\nduration:0.019\nstart:2017-03-24 08:11:51\nend:2017-03-24 08:11:52'></div><div class='bar' style='background-color:#90BBD7;height:12.364px;width:160.000px;left:420px; top:1450.814px;'title='8 threads\nduration:0.287\nstart:2017-03-24 08:11:52\nend:2017-03-24 08:12:10'></div><div class='bar' style='background-color:#90BBD7;height:62.675px;width:140.000px;left:420px; top:1465.178px;'title='7 threads\nduration:1.294\nstart:2017-03-24 08:12:10\nend:2017-03-24 08:13:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1529.853px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:13:27\nend:2017-03-24 08:13:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1529.877px;'title='7 threads\nduration:0.019\nstart:2017-03-24 08:13:27\nend:2017-03-24 08:13:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1530.829px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:13:28\nend:2017-03-24 08:13:28'></div><div class='bar' style='background-color:#90BBD7;height:8.307px;width:140.000px;left:420px; top:1530.852px;'title='7 threads\nduration:0.206\nstart:2017-03-24 08:13:28\nend:2017-03-24 08:13:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1541.159px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:13:41\nend:2017-03-24 08:13:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1541.183px;'title='7 threads\nduration:0.011\nstart:2017-03-24 08:13:41\nend:2017-03-24 08:13:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1541.738px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:13:41\nend:2017-03-24 08:13:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1541.761px;'title='7 threads\nduration:0.013\nstart:2017-03-24 08:13:41\nend:2017-03-24 08:13:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1542.424px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:13:42\nend:2017-03-24 08:13:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1542.448px;'title='7 threads\nduration:0.007\nstart:2017-03-24 08:13:42\nend:2017-03-24 08:13:43'></div><div class='bar' style='background-color:#90BBD7;height:70.792px;width:120.000px;left:420px; top:1542.777px;'title='6 threads\nduration:1.456\nstart:2017-03-24 08:13:43\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1615.569px;'title='5 threads\nduration:0.000\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1615.594px;'title='6 threads\nduration:0.001\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1615.639px;'title='7 threads\nduration:0.001\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1615.689px;'title='8 threads\nduration:0.001\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1615.740px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1615.788px;'title='10 threads\nduration:0.007\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:11'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1616.119px;'title='9 threads\nduration:0.042\nstart:2017-03-24 08:15:11\nend:2017-03-24 08:15:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1618.234px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:15:13\nend:2017-03-24 08:15:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1618.258px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:15:13\nend:2017-03-24 08:15:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1618.306px;'title='10 threads\nduration:0.013\nstart:2017-03-24 08:15:13\nend:2017-03-24 08:15:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1618.932px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:15:14\nend:2017-03-24 08:15:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1618.957px;'title='10 threads\nduration:0.028\nstart:2017-03-24 08:15:14\nend:2017-03-24 08:15:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1620.376px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:15:16\nend:2017-03-24 08:15:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1620.401px;'title='10 threads\nduration:0.033\nstart:2017-03-24 08:15:16\nend:2017-03-24 08:15:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1622.072px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:15:18\nend:2017-03-24 08:15:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1622.095px;'title='10 threads\nduration:0.061\nstart:2017-03-24 08:15:18\nend:2017-03-24 08:15:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1625.151px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1625.175px;'title='10 threads\nduration:0.003\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1625.319px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:22'></div><div class='bar' style='background-color:#90BBD7;height:6.009px;width:200.000px;left:420px; top:1625.343px;'title='10 threads\nduration:0.160\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1633.352px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:15:31\nend:2017-03-24 08:15:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1633.376px;'title='10 threads\nduration:0.012\nstart:2017-03-24 08:15:31\nend:2017-03-24 08:15:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1633.997px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:15:32\nend:2017-03-24 08:15:32'></div><div class='bar' style='background-color:#90BBD7;height:44.923px;width:200.000px;left:420px; top:1634.021px;'title='10 threads\nduration:0.938\nstart:2017-03-24 08:15:32\nend:2017-03-24 08:16:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1680.944px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:16:28\nend:2017-03-24 08:16:29'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1680.966px;'title='10 threads\nduration:0.040\nstart:2017-03-24 08:16:29\nend:2017-03-24 08:16:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1682.967px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:16:31\nend:2017-03-24 08:16:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1682.990px;'title='10 threads\nduration:0.008\nstart:2017-03-24 08:16:31\nend:2017-03-24 08:16:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1683.372px;'title='9 threads\nduration:0.006\nstart:2017-03-24 08:16:31\nend:2017-03-24 08:16:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1683.647px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:16:32\nend:2017-03-24 08:16:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1683.671px;'title='9 threads\nduration:0.017\nstart:2017-03-24 08:16:32\nend:2017-03-24 08:16:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1684.509px;'title='8 threads\nduration:0.015\nstart:2017-03-24 08:16:33\nend:2017-03-24 08:16:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1685.243px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:16:34\nend:2017-03-24 08:16:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1685.267px;'title='8 threads\nduration:0.022\nstart:2017-03-24 08:16:34\nend:2017-03-24 08:16:35'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1686.344px;'title='7 threads\nduration:0.085\nstart:2017-03-24 08:16:35\nend:2017-03-24 08:16:40'></div><div class='bar' style='background-color:#90BBD7;height:34.346px;width:120.000px;left:420px; top:1690.597px;'title='6 threads\nduration:0.727\nstart:2017-03-24 08:16:40\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1726.943px;'title='5 threads\nduration:0.000\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1726.964px;'title='6 threads\nduration:0.001\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1727.009px;'title='7 threads\nduration:0.001\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1727.056px;'title='8 threads\nduration:0.001\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1727.107px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1727.158px;'title='10 threads\nduration:0.005\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1727.404px;'title='9 threads\nduration:0.051\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1729.936px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:17:27\nend:2017-03-24 08:17:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1729.959px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:17:27\nend:2017-03-24 08:17:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1730.007px;'title='10 threads\nduration:0.014\nstart:2017-03-24 08:17:27\nend:2017-03-24 08:17:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1730.715px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:17:28\nend:2017-03-24 08:17:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1730.739px;'title='10 threads\nduration:0.029\nstart:2017-03-24 08:17:28\nend:2017-03-24 08:17:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1732.197px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:17:30\nend:2017-03-24 08:17:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1732.220px;'title='10 threads\nduration:0.039\nstart:2017-03-24 08:17:30\nend:2017-03-24 08:17:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1734.147px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:17:32\nend:2017-03-24 08:17:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1734.170px;'title='10 threads\nduration:0.026\nstart:2017-03-24 08:17:32\nend:2017-03-24 08:17:34'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1735.455px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:17:34\nend:2017-03-24 08:17:34'></div><div class='bar' style='background-color:#90BBD7;height:4.826px;width:200.000px;left:420px; top:1735.478px;'title='10 threads\nduration:0.137\nstart:2017-03-24 08:17:34\nend:2017-03-24 08:17:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1742.304px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:17:42\nend:2017-03-24 08:17:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1742.326px;'title='10 threads\nduration:0.012\nstart:2017-03-24 08:17:42\nend:2017-03-24 08:17:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1742.937px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:17:43\nend:2017-03-24 08:17:43'></div><div class='bar' style='background-color:#90BBD7;height:52.600px;width:200.000px;left:420px; top:1742.960px;'title='10 threads\nduration:1.092\nstart:2017-03-24 08:17:43\nend:2017-03-24 08:18:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1797.560px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:18:48\nend:2017-03-24 08:18:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1797.580px;'title='10 threads\nduration:0.026\nstart:2017-03-24 08:18:48\nend:2017-03-24 08:18:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1798.860px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:18:50\nend:2017-03-24 08:18:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1798.882px;'title='10 threads\nduration:0.010\nstart:2017-03-24 08:18:50\nend:2017-03-24 08:18:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1799.368px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:18:51\nend:2017-03-24 08:18:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:1799.391px;'title='10 threads\nduration:0.003\nstart:2017-03-24 08:18:51\nend:2017-03-24 08:18:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:1799.544px;'title='9 threads\nduration:0.015\nstart:2017-03-24 08:18:51\nend:2017-03-24 08:18:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:1800.308px;'title='8 threads\nduration:0.011\nstart:2017-03-24 08:18:52\nend:2017-03-24 08:18:52'></div><div class='bar' style='background-color:#90BBD7;height:35.614px;width:140.000px;left:420px; top:1800.865px;'title='7 threads\nduration:0.752\nstart:2017-03-24 08:18:52\nend:2017-03-24 08:19:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1838.478px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:19:38\nend:2017-03-24 08:19:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1838.500px;'title='7 threads\nduration:0.095\nstart:2017-03-24 08:19:38\nend:2017-03-24 08:19:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1843.241px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:19:43\nend:2017-03-24 08:19:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:1843.264px;'title='7 threads\nduration:0.019\nstart:2017-03-24 08:19:43\nend:2017-03-24 08:19:44'></div><div class='bar' style='background-color:#90BBD7;height:33.261px;width:120.000px;left:420px; top:1844.209px;'title='6 threads\nduration:0.705\nstart:2017-03-24 08:19:44\nend:2017-03-24 08:20:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1879.470px;'title='5 threads\nduration:0.000\nstart:2017-03-24 08:20:27\nend:2017-03-24 08:20:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1879.493px;'title='6 threads\nduration:0.018\nstart:2017-03-24 08:20:27\nend:2017-03-24 08:20:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1880.405px;'title='5 threads\nduration:0.000\nstart:2017-03-24 08:20:28\nend:2017-03-24 08:20:28'></div><div class='bar' style='background-color:#90BBD7;height:8.094px;width:120.000px;left:420px; top:1880.428px;'title='6 threads\nduration:0.202\nstart:2017-03-24 08:20:28\nend:2017-03-24 08:20:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1890.522px;'title='5 threads\nduration:0.000\nstart:2017-03-24 08:20:40\nend:2017-03-24 08:20:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1890.545px;'title='6 threads\nduration:0.011\nstart:2017-03-24 08:20:40\nend:2017-03-24 08:20:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1891.099px;'title='5 threads\nduration:0.001\nstart:2017-03-24 08:20:41\nend:2017-03-24 08:20:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1891.124px;'title='6 threads\nduration:0.013\nstart:2017-03-24 08:20:41\nend:2017-03-24 08:20:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1891.796px;'title='5 threads\nduration:0.001\nstart:2017-03-24 08:20:42\nend:2017-03-24 08:20:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:1891.823px;'title='6 threads\nduration:0.007\nstart:2017-03-24 08:20:42\nend:2017-03-24 08:20:42'></div><div class='bar' style='background-color:#90BBD7;height:35.200px;width:100.000px;left:420px; top:1892.154px;'title='5 threads\nduration:0.744\nstart:2017-03-24 08:20:42\nend:2017-03-24 08:21:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:1929.354px;'title='4 threads\nduration:0.001\nstart:2017-03-24 08:21:27\nend:2017-03-24 08:21:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1929.383px;'title='5 threads\nduration:0.018\nstart:2017-03-24 08:21:27\nend:2017-03-24 08:21:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:1930.296px;'title='4 threads\nduration:0.001\nstart:2017-03-24 08:21:28\nend:2017-03-24 08:21:28'></div><div class='bar' style='background-color:#90BBD7;height:9.360px;width:100.000px;left:420px; top:1930.322px;'title='5 threads\nduration:0.227\nstart:2017-03-24 08:21:28\nend:2017-03-24 08:21:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:1941.682px;'title='4 threads\nduration:0.000\nstart:2017-03-24 08:21:41\nend:2017-03-24 08:21:41'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1941.705px;'title='5 threads\nduration:0.011\nstart:2017-03-24 08:21:41\nend:2017-03-24 08:21:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:1942.259px;'title='4 threads\nduration:0.000\nstart:2017-03-24 08:21:42\nend:2017-03-24 08:21:42'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1942.282px;'title='5 threads\nduration:0.012\nstart:2017-03-24 08:21:42\nend:2017-03-24 08:21:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:1942.903px;'title='4 threads\nduration:0.000\nstart:2017-03-24 08:21:43\nend:2017-03-24 08:21:43'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:1942.924px;'title='5 threads\nduration:0.007\nstart:2017-03-24 08:21:43\nend:2017-03-24 08:21:43'></div><div class='bar' style='background-color:#90BBD7;height:130.210px;width:80.000px;left:420px; top:1943.253px;'title='4 threads\nduration:2.644\nstart:2017-03-24 08:21:43\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:2075.462px;'title='3 threads\nduration:0.000\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2075.486px;'title='4 threads\nduration:0.001\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2075.534px;'title='5 threads\nduration:0.001\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2075.583px;'title='6 threads\nduration:0.001\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2075.628px;'title='7 threads\nduration:0.001\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2075.676px;'title='8 threads\nduration:0.007\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2076.009px;'title='7 threads\nduration:0.059\nstart:2017-03-24 08:24:23\nend:2017-03-24 08:24:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2078.958px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2078.981px;'title='7 threads\nduration:0.001\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2079.029px;'title='8 threads\nduration:0.001\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:26'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2079.074px;'title='9 threads\nduration:0.012\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2079.698px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:24:27\nend:2017-03-24 08:24:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2079.720px;'title='9 threads\nduration:0.033\nstart:2017-03-24 08:24:27\nend:2017-03-24 08:24:29'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2081.346px;'title='8 threads\nduration:0.044\nstart:2017-03-24 08:24:29\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2083.542px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2083.566px;'title='8 threads\nduration:0.002\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2083.651px;'title='9 threads\nduration:0.002\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2083.736px;'title='10 threads\nduration:0.003\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2083.888px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2083.912px;'title='10 threads\nduration:0.009\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2084.363px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2084.386px;'title='10 threads\nduration:0.003\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2084.538px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2084.562px;'title='10 threads\nduration:0.056\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2087.372px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:24:36\nend:2017-03-24 08:24:36'></div><div class='bar' style='background-color:#90BBD7;height:3.105px;width:200.000px;left:420px; top:2087.395px;'title='10 threads\nduration:0.102\nstart:2017-03-24 08:24:36\nend:2017-03-24 08:24:42'></div><div class='bar' style='background-color:#90BBD7;height:35.152px;width:180.000px;left:420px; top:2092.500px;'title='9 threads\nduration:0.743\nstart:2017-03-24 08:24:42\nend:2017-03-24 08:25:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2129.652px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:25:27\nend:2017-03-24 08:25:27'></div><div class='bar' style='background-color:#90BBD7;height:3.360px;width:180.000px;left:420px; top:2129.676px;'title='9 threads\nduration:0.107\nstart:2017-03-24 08:25:27\nend:2017-03-24 08:25:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2135.036px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:25:33\nend:2017-03-24 08:25:33'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2135.060px;'title='9 threads\nduration:0.019\nstart:2017-03-24 08:25:33\nend:2017-03-24 08:25:35'></div><div class='bar' style='background-color:#90BBD7;height:32.575px;width:160.000px;left:420px; top:2135.994px;'title='8 threads\nduration:0.691\nstart:2017-03-24 08:25:35\nend:2017-03-24 08:26:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2170.569px;'title='7 threads\nduration:0.020\nstart:2017-03-24 08:26:16\nend:2017-03-24 08:26:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2171.544px;'title='6 threads\nduration:0.030\nstart:2017-03-24 08:26:17\nend:2017-03-24 08:26:19'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2173.020px;'title='5 threads\nduration:0.063\nstart:2017-03-24 08:26:19\nend:2017-03-24 08:26:23'></div><div class='bar' style='background-color:#90BBD7;height:20.406px;width:80.000px;left:420px; top:2176.177px;'title='4 threads\nduration:0.448\nstart:2017-03-24 08:26:23\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:2198.583px;'title='3 threads\nduration:0.000\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2198.606px;'title='4 threads\nduration:0.001\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2198.651px;'title='5 threads\nduration:0.001\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2198.698px;'title='6 threads\nduration:0.001\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2198.749px;'title='7 threads\nduration:0.001\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2198.799px;'title='8 threads\nduration:0.008\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2199.183px;'title='7 threads\nduration:0.060\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2202.181px;'title='6 threads\nduration:0.001\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:26:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2202.212px;'title='7 threads\nduration:0.001\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:26:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2202.264px;'title='8 threads\nduration:0.001\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:26:54'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2202.320px;'title='9 threads\nduration:0.014\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:26:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2203.043px;'title='8 threads\nduration:0.001\nstart:2017-03-24 08:26:55\nend:2017-03-24 08:26:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2203.073px;'title='9 threads\nduration:0.043\nstart:2017-03-24 08:26:55\nend:2017-03-24 08:26:58'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2205.216px;'title='8 threads\nduration:0.041\nstart:2017-03-24 08:26:58\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2207.259px;'title='7 threads\nduration:0.004\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2207.452px;'title='6 threads\nduration:0.001\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2207.480px;'title='7 threads\nduration:0.002\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2207.564px;'title='8 threads\nduration:0.002\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2207.647px;'title='9 threads\nduration:0.002\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2207.728px;'title='10 threads\nduration:0.011\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2208.265px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:01'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2208.287px;'title='10 threads\nduration:0.064\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:05'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2211.480px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:27:05\nend:2017-03-24 08:27:05'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2211.503px;'title='10 threads\nduration:0.013\nstart:2017-03-24 08:27:05\nend:2017-03-24 08:27:06'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2212.128px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:27:06\nend:2017-03-24 08:27:06'></div><div class='bar' style='background-color:#90BBD7;height:3.955px;width:200.000px;left:420px; top:2212.150px;'title='10 threads\nduration:0.119\nstart:2017-03-24 08:27:06\nend:2017-03-24 08:27:13'></div><div class='bar' style='background-color:#90BBD7;height:34.898px;width:180.000px;left:420px; top:2218.105px;'title='9 threads\nduration:0.738\nstart:2017-03-24 08:27:13\nend:2017-03-24 08:27:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2255.003px;'title='8 threads\nduration:0.001\nstart:2017-03-24 08:27:57\nend:2017-03-24 08:27:57'></div><div class='bar' style='background-color:#90BBD7;height:3.823px;width:180.000px;left:420px; top:2255.030px;'title='9 threads\nduration:0.116\nstart:2017-03-24 08:27:57\nend:2017-03-24 08:28:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2260.853px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:28:04\nend:2017-03-24 08:28:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2260.877px;'title='9 threads\nduration:0.019\nstart:2017-03-24 08:28:04\nend:2017-03-24 08:28:06'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2261.833px;'title='8 threads\nduration:0.030\nstart:2017-03-24 08:28:06\nend:2017-03-24 08:28:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2263.318px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2263.342px;'title='8 threads\nduration:0.001\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2263.390px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2263.437px;'title='10 threads\nduration:0.007\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2263.769px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:28:08\nend:2017-03-24 08:28:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2263.791px;'title='10 threads\nduration:0.079\nstart:2017-03-24 08:28:08\nend:2017-03-24 08:28:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2267.744px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:28:13\nend:2017-03-24 08:28:13'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2267.773px;'title='10 threads\nduration:0.049\nstart:2017-03-24 08:28:13\nend:2017-03-24 08:28:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2270.202px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:28:16\nend:2017-03-24 08:28:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2270.226px;'title='10 threads\nduration:0.039\nstart:2017-03-24 08:28:16\nend:2017-03-24 08:28:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2272.158px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:28:18\nend:2017-03-24 08:28:18'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2272.178px;'title='10 threads\nduration:0.043\nstart:2017-03-24 08:28:18\nend:2017-03-24 08:28:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2274.351px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:28:21\nend:2017-03-24 08:28:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2274.374px;'title='10 threads\nduration:0.035\nstart:2017-03-24 08:28:21\nend:2017-03-24 08:28:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2276.134px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:28:23\nend:2017-03-24 08:28:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2276.158px;'title='10 threads\nduration:0.025\nstart:2017-03-24 08:28:23\nend:2017-03-24 08:28:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2277.391px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:28:24\nend:2017-03-24 08:28:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2277.419px;'title='10 threads\nduration:0.098\nstart:2017-03-24 08:28:24\nend:2017-03-24 08:28:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2282.339px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:28:30\nend:2017-03-24 08:28:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2282.367px;'title='10 threads\nduration:0.013\nstart:2017-03-24 08:28:30\nend:2017-03-24 08:28:31'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2282.996px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:28:31\nend:2017-03-24 08:28:31'></div><div class='bar' style='background-color:#90BBD7;height:9.357px;width:200.000px;left:420px; top:2283.019px;'title='10 threads\nduration:0.227\nstart:2017-03-24 08:28:31\nend:2017-03-24 08:28:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2294.376px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:28:45\nend:2017-03-24 08:28:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2294.399px;'title='10 threads\nduration:0.059\nstart:2017-03-24 08:28:45\nend:2017-03-24 08:28:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2297.361px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:28:48\nend:2017-03-24 08:28:48'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2297.383px;'title='10 threads\nduration:0.046\nstart:2017-03-24 08:28:48\nend:2017-03-24 08:28:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2299.685px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:28:51\nend:2017-03-24 08:28:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2299.711px;'title='10 threads\nduration:0.017\nstart:2017-03-24 08:28:51\nend:2017-03-24 08:28:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2300.546px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:28:52\nend:2017-03-24 08:28:52'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2300.580px;'title='10 threads\nduration:0.045\nstart:2017-03-24 08:28:52\nend:2017-03-24 08:28:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2302.841px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:28:55\nend:2017-03-24 08:28:55'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2302.874px;'title='10 threads\nduration:0.016\nstart:2017-03-24 08:28:55\nend:2017-03-24 08:28:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2303.684px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:28:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2303.756px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:28:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2303.779px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:28:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2303.826px;'title='10 threads\nduration:0.093\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:29:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2308.498px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:29:02\nend:2017-03-24 08:29:02'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2308.524px;'title='10 threads\nduration:0.095\nstart:2017-03-24 08:29:02\nend:2017-03-24 08:29:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2313.271px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:29:07\nend:2017-03-24 08:29:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2313.294px;'title='10 threads\nduration:0.007\nstart:2017-03-24 08:29:07\nend:2017-03-24 08:29:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2313.639px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:29:08\nend:2017-03-24 08:29:08'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2313.662px;'title='10 threads\nduration:0.071\nstart:2017-03-24 08:29:08\nend:2017-03-24 08:29:12'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2317.191px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:29:12\nend:2017-03-24 08:29:12'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2317.214px;'title='10 threads\nduration:0.085\nstart:2017-03-24 08:29:12\nend:2017-03-24 08:29:17'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2321.486px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:29:17\nend:2017-03-24 08:29:17'></div><div class='bar' style='background-color:#90BBD7;height:7.191px;width:200.000px;left:420px; top:2321.512px;'title='10 threads\nduration:0.184\nstart:2017-03-24 08:29:17\nend:2017-03-24 08:29:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2330.703px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:29:28\nend:2017-03-24 08:29:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2330.724px;'title='10 threads\nduration:0.016\nstart:2017-03-24 08:29:28\nend:2017-03-24 08:29:29'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2331.543px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:29:29\nend:2017-03-24 08:29:29'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2331.565px;'title='10 threads\nduration:0.013\nstart:2017-03-24 08:29:29\nend:2017-03-24 08:29:30'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2332.229px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:29:30\nend:2017-03-24 08:29:30'></div><div class='bar' style='background-color:#90BBD7;height:3.033px;width:200.000px;left:420px; top:2332.251px;'title='10 threads\nduration:0.101\nstart:2017-03-24 08:29:30\nend:2017-03-24 08:29:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2337.283px;'title='9 threads\nduration:0.001\nstart:2017-03-24 08:29:36\nend:2017-03-24 08:29:36'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2337.312px;'title='10 threads\nduration:0.013\nstart:2017-03-24 08:29:36\nend:2017-03-24 08:29:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2337.965px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:29:37\nend:2017-03-24 08:29:37'></div><div class='bar' style='background-color:#90BBD7;height:19.986px;width:200.000px;left:420px; top:2337.986px;'title='10 threads\nduration:0.440\nstart:2017-03-24 08:29:37\nend:2017-03-24 08:30:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2359.972px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:30:03\nend:2017-03-24 08:30:03'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2359.994px;'title='10 threads\nduration:0.059\nstart:2017-03-24 08:30:03\nend:2017-03-24 08:30:07'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2362.963px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:30:07\nend:2017-03-24 08:30:07'></div><div class='bar' style='background-color:#90BBD7;height:4.471px;width:200.000px;left:420px; top:2362.987px;'title='10 threads\nduration:0.129\nstart:2017-03-24 08:30:07\nend:2017-03-24 08:30:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2369.459px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:30:15\nend:2017-03-24 08:30:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2369.482px;'title='10 threads\nduration:0.098\nstart:2017-03-24 08:30:15\nend:2017-03-24 08:30:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2374.385px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:30:21\nend:2017-03-24 08:30:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2374.409px;'title='10 threads\nduration:0.041\nstart:2017-03-24 08:30:21\nend:2017-03-24 08:30:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2376.462px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:30:23\nend:2017-03-24 08:30:23'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2376.484px;'title='10 threads\nduration:0.019\nstart:2017-03-24 08:30:23\nend:2017-03-24 08:30:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2377.456px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:30:24\nend:2017-03-24 08:30:24'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2377.478px;'title='10 threads\nduration:0.038\nstart:2017-03-24 08:30:24\nend:2017-03-24 08:30:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2379.394px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:30:27\nend:2017-03-24 08:30:27'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2379.416px;'title='10 threads\nduration:0.019\nstart:2017-03-24 08:30:27\nend:2017-03-24 08:30:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2380.374px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:30:28\nend:2017-03-24 08:30:28'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2380.396px;'title='10 threads\nduration:0.073\nstart:2017-03-24 08:30:28\nend:2017-03-24 08:30:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:180.000px;left:420px; top:2384.057px;'title='9 threads\nduration:0.000\nstart:2017-03-24 08:30:32\nend:2017-03-24 08:30:32'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:200.000px;left:420px; top:2384.080px;'title='10 threads\nduration:0.020\nstart:2017-03-24 08:30:32\nend:2017-03-24 08:30:33'></div><div class='bar' style='background-color:#90BBD7;height:3.484px;width:180.000px;left:420px; top:2385.056px;'title='9 threads\nduration:0.110\nstart:2017-03-24 08:30:33\nend:2017-03-24 08:30:40'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2390.540px;'title='8 threads\nduration:0.061\nstart:2017-03-24 08:30:40\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2393.568px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2393.590px;'title='8 threads\nduration:0.011\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2394.144px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:44'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:160.000px;left:420px; top:2394.166px;'title='8 threads\nduration:0.007\nstart:2017-03-24 08:30:44\nend:2017-03-24 08:30:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2394.497px;'title='7 threads\nduration:0.008\nstart:2017-03-24 08:30:45\nend:2017-03-24 08:30:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2394.883px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:30:45\nend:2017-03-24 08:30:45'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:140.000px;left:420px; top:2394.905px;'title='7 threads\nduration:0.007\nstart:2017-03-24 08:30:45\nend:2017-03-24 08:30:46'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:120.000px;left:420px; top:2395.236px;'title='6 threads\nduration:0.089\nstart:2017-03-24 08:30:46\nend:2017-03-24 08:30:51'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2399.691px;'title='5 threads\nduration:0.092\nstart:2017-03-24 08:30:51\nend:2017-03-24 08:30:56'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2404.278px;'title='4 threads\nduration:0.000\nstart:2017-03-24 08:30:56\nend:2017-03-24 08:30:57'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2404.300px;'title='5 threads\nduration:0.019\nstart:2017-03-24 08:30:57\nend:2017-03-24 08:30:58'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2405.252px;'title='4 threads\nduration:0.000\nstart:2017-03-24 08:30:58\nend:2017-03-24 08:30:58'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:100.000px;left:420px; top:2405.274px;'title='5 threads\nduration:0.035\nstart:2017-03-24 08:30:58\nend:2017-03-24 08:31:00'></div><div class='bar' style='background-color:#90BBD7;height:10.271px;width:80.000px;left:420px; top:2407.009px;'title='4 threads\nduration:0.245\nstart:2017-03-24 08:31:00\nend:2017-03-24 08:31:14'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:2419.280px;'title='3 threads\nduration:0.000\nstart:2017-03-24 08:31:14\nend:2017-03-24 08:31:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2419.303px;'title='4 threads\nduration:0.011\nstart:2017-03-24 08:31:15\nend:2017-03-24 08:31:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:2419.854px;'title='3 threads\nduration:0.000\nstart:2017-03-24 08:31:15\nend:2017-03-24 08:31:15'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2419.875px;'title='4 threads\nduration:0.013\nstart:2017-03-24 08:31:15\nend:2017-03-24 08:31:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:2420.538px;'title='3 threads\nduration:0.000\nstart:2017-03-24 08:31:16\nend:2017-03-24 08:31:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:80.000px;left:420px; top:2420.560px;'title='4 threads\nduration:0.007\nstart:2017-03-24 08:31:16\nend:2017-03-24 08:31:16'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:60.000px;left:420px; top:2420.890px;'title='3 threads\nduration:0.092\nstart:2017-03-24 08:31:16\nend:2017-03-24 08:31:22'></div><div class='bar' style='background-color:#90BBD7;height:47.085px;width:40.000px;left:420px; top:2425.512px;'title='2 threads\nduration:0.982\nstart:2017-03-24 08:31:22\nend:2017-03-24 08:32:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2474.597px;'title='1 threads\nduration:0.000\nstart:2017-03-24 08:32:21\nend:2017-03-24 08:32:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:2474.619px;'title='2 threads\nduration:0.024\nstart:2017-03-24 08:32:21\nend:2017-03-24 08:32:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2475.825px;'title='1 threads\nduration:0.000\nstart:2017-03-24 08:32:22\nend:2017-03-24 08:32:22'></div><div class='bar' style='background-color:#90BBD7;height:10.190px;width:40.000px;left:420px; top:2475.846px;'title='2 threads\nduration:0.244\nstart:2017-03-24 08:32:22\nend:2017-03-24 08:32:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2488.036px;'title='1 threads\nduration:0.000\nstart:2017-03-24 08:32:37\nend:2017-03-24 08:32:37'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:2488.058px;'title='2 threads\nduration:0.011\nstart:2017-03-24 08:32:37\nend:2017-03-24 08:32:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2488.613px;'title='1 threads\nduration:0.001\nstart:2017-03-24 08:32:38\nend:2017-03-24 08:32:38'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:2488.640px;'title='2 threads\nduration:0.014\nstart:2017-03-24 08:32:38\nend:2017-03-24 08:32:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2489.348px;'title='1 threads\nduration:0.000\nstart:2017-03-24 08:32:39\nend:2017-03-24 08:32:39'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:40.000px;left:420px; top:2489.370px;'title='2 threads\nduration:0.007\nstart:2017-03-24 08:32:39\nend:2017-03-24 08:32:39'></div><div class='bar' style='background-color:#90BBD7;height:68.544px;width:20.000px;left:420px; top:2489.701px;'title='1 threads\nduration:1.411\nstart:2017-03-24 08:32:39\nend:2017-03-24 08:34:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2560.245px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:34:04\nend:2017-03-24 08:34:04'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2560.264px;'title='1 threads\nduration:0.019\nstart:2017-03-24 08:34:04\nend:2017-03-24 08:34:05'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2561.217px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:34:05\nend:2017-03-24 08:34:05'></div><div class='bar' style='background-color:#90BBD7;height:10.606px;width:20.000px;left:420px; top:2561.238px;'title='1 threads\nduration:0.252\nstart:2017-03-24 08:34:05\nend:2017-03-24 08:34:20'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2573.845px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:34:20\nend:2017-03-24 08:34:20'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2573.866px;'title='1 threads\nduration:0.011\nstart:2017-03-24 08:34:20\nend:2017-03-24 08:34:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2574.416px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:34:21\nend:2017-03-24 08:34:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2574.436px;'title='1 threads\nduration:0.013\nstart:2017-03-24 08:34:21\nend:2017-03-24 08:34:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2575.096px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:34:21\nend:2017-03-24 08:34:21'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:20.000px;left:420px; top:2575.117px;'title='1 threads\nduration:0.007\nstart:2017-03-24 08:34:21\nend:2017-03-24 08:34:22'></div><div class='bar' style='background-color:#90BBD7;height:3.000px;width:0.000px;left:420px; top:2575.448px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:34:22\nend:2017-03-24 08:34:22'></div><p class='time' style='top:198px;left:420px;'>Threads</p><div class='bar' style='background-color:#03969D;height:5.918px;width:0.000px;left:420px; top:220.000px;'title='0 threads\nduration:0.158\nstart:2017-03-24 07:47:15\nend:2017-03-24 07:47:25'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:227.918px;'title='1 threads\nduration:0.039\nstart:2017-03-24 07:47:25\nend:2017-03-24 07:47:27'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:0.000px;left:420px; top:229.852px;'title='0 threads\nduration:0.000\nstart:2017-03-24 07:47:27\nend:2017-03-24 07:47:27'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:229.877px;'title='1 threads\nduration:0.099\nstart:2017-03-24 07:47:27\nend:2017-03-24 07:47:33'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:234.831px;'title='2 threads\nduration:0.046\nstart:2017-03-24 07:47:33\nend:2017-03-24 07:47:36'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:237.141px;'title='1 threads\nduration:0.031\nstart:2017-03-24 07:47:36\nend:2017-03-24 07:47:38'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:238.701px;'title='2 threads\nduration:0.058\nstart:2017-03-24 07:47:38\nend:2017-03-24 07:47:41'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:241.605px;'title='1 threads\nduration:0.032\nstart:2017-03-24 07:47:41\nend:2017-03-24 07:47:43'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:243.190px;'title='2 threads\nduration:0.030\nstart:2017-03-24 07:47:43\nend:2017-03-24 07:47:45'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:244.680px;'title='3 threads\nduration:0.045\nstart:2017-03-24 07:47:45\nend:2017-03-24 07:47:48'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:246.938px;'title='2 threads\nduration:0.036\nstart:2017-03-24 07:47:48\nend:2017-03-24 07:47:50'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:248.755px;'title='1 threads\nduration:0.000\nstart:2017-03-24 07:47:50\nend:2017-03-24 07:47:50'></div><div class='bar' style='background-color:#03969D;height:9.182px;width:40.000px;left:420px; top:248.779px;'title='2 threads\nduration:0.224\nstart:2017-03-24 07:47:50\nend:2017-03-24 07:48:03'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:259.962px;'title='3 threads\nduration:0.037\nstart:2017-03-24 07:48:03\nend:2017-03-24 07:48:06'></div><div class='bar' style='background-color:#03969D;height:44.003px;width:80.000px;left:420px; top:261.805px;'title='4 threads\nduration:0.920\nstart:2017-03-24 07:48:06\nend:2017-03-24 07:49:01'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:307.808px;'title='5 threads\nduration:0.054\nstart:2017-03-24 07:49:01\nend:2017-03-24 07:49:04'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:80.000px;left:420px; top:310.532px;'title='4 threads\nduration:0.009\nstart:2017-03-24 07:49:04\nend:2017-03-24 07:49:05'></div><div class='bar' style='background-color:#03969D;height:7.940px;width:100.000px;left:420px; top:310.984px;'title='5 threads\nduration:0.199\nstart:2017-03-24 07:49:05\nend:2017-03-24 07:49:16'></div><div class='bar' style='background-color:#03969D;height:43.111px;width:120.000px;left:420px; top:320.923px;'title='6 threads\nduration:0.902\nstart:2017-03-24 07:49:16\nend:2017-03-24 07:50:11'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:366.034px;'title='7 threads\nduration:0.060\nstart:2017-03-24 07:50:11\nend:2017-03-24 07:50:14'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:369.036px;'title='8 threads\nduration:0.011\nstart:2017-03-24 07:50:14\nend:2017-03-24 07:50:15'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:369.577px;'title='7 threads\nduration:0.000\nstart:2017-03-24 07:50:15\nend:2017-03-24 07:50:15'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:369.601px;'title='8 threads\nduration:0.064\nstart:2017-03-24 07:50:15\nend:2017-03-24 07:50:19'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:372.822px;'title='7 threads\nduration:0.001\nstart:2017-03-24 07:50:19\nend:2017-03-24 07:50:19'></div><div class='bar' style='background-color:#03969D;height:132.512px;width:160.000px;left:420px; top:372.896px;'title='8 threads\nduration:2.690\nstart:2017-03-24 07:50:19\nend:2017-03-24 07:53:00'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:507.408px;'title='7 threads\nduration:0.001\nstart:2017-03-24 07:53:00\nend:2017-03-24 07:53:00'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:507.434px;'title='8 threads\nduration:0.029\nstart:2017-03-24 07:53:00\nend:2017-03-24 07:53:02'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:508.896px;'title='7 threads\nduration:0.000\nstart:2017-03-24 07:53:02\nend:2017-03-24 07:53:02'></div><div class='bar' style='background-color:#03969D;height:275.136px;width:160.000px;left:420px; top:508.921px;'title='8 threads\nduration:5.543\nstart:2017-03-24 07:53:02\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:786.057px;'title='7 threads\nduration:0.002\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:786.134px;'title='8 threads\nduration:0.007\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:35'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:180.000px;left:420px; top:786.460px;'title='9 threads\nduration:0.029\nstart:2017-03-24 07:58:35\nend:2017-03-24 07:58:37'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:787.925px;'title='8 threads\nduration:0.014\nstart:2017-03-24 07:58:37\nend:2017-03-24 07:58:38'></div><div class='bar' style='background-color:#03969D;height:5.393px;width:140.000px;left:420px; top:788.610px;'title='7 threads\nduration:0.148\nstart:2017-03-24 07:58:38\nend:2017-03-24 07:58:47'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:796.002px;'title='6 threads\nduration:0.000\nstart:2017-03-24 07:58:47\nend:2017-03-24 07:58:47'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:796.027px;'title='7 threads\nduration:0.044\nstart:2017-03-24 07:58:47\nend:2017-03-24 07:58:49'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:798.245px;'title='6 threads\nduration:0.000\nstart:2017-03-24 07:58:49\nend:2017-03-24 07:58:49'></div><div class='bar' style='background-color:#03969D;height:52.144px;width:140.000px;left:420px; top:798.269px;'title='7 threads\nduration:1.083\nstart:2017-03-24 07:58:49\nend:2017-03-24 07:59:54'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:852.413px;'title='8 threads\nduration:0.011\nstart:2017-03-24 07:59:54\nend:2017-03-24 07:59:55'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:180.000px;left:420px; top:852.971px;'title='9 threads\nduration:0.066\nstart:2017-03-24 07:59:55\nend:2017-03-24 07:59:59'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:856.248px;'title='8 threads\nduration:0.001\nstart:2017-03-24 07:59:59\nend:2017-03-24 07:59:59'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:180.000px;left:420px; top:856.299px;'title='9 threads\nduration:0.014\nstart:2017-03-24 07:59:59\nend:2017-03-24 08:00:00'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:857.002px;'title='8 threads\nduration:0.001\nstart:2017-03-24 08:00:00\nend:2017-03-24 08:00:00'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:180.000px;left:420px; top:857.027px;'title='9 threads\nduration:0.016\nstart:2017-03-24 08:00:00\nend:2017-03-24 08:00:01'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:857.815px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:00:01\nend:2017-03-24 08:00:01'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:180.000px;left:420px; top:857.837px;'title='9 threads\nduration:0.013\nstart:2017-03-24 08:00:01\nend:2017-03-24 08:00:02'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:858.490px;'title='8 threads\nduration:0.015\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:00:02'></div><div class='bar' style='background-color:#03969D;height:108.203px;width:140.000px;left:420px; top:859.227px;'title='7 threads\nduration:2.204\nstart:2017-03-24 08:00:02\nend:2017-03-24 08:02:15'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:969.430px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:02:15\nend:2017-03-24 08:02:15'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:969.453px;'title='7 threads\nduration:0.050\nstart:2017-03-24 08:02:15\nend:2017-03-24 08:02:18'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:971.964px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:02:18\nend:2017-03-24 08:02:18'></div><div class='bar' style='background-color:#03969D;height:18.952px;width:140.000px;left:420px; top:971.988px;'title='7 threads\nduration:0.419\nstart:2017-03-24 08:02:18\nend:2017-03-24 08:02:43'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:992.940px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:02:43\nend:2017-03-24 08:02:43'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:992.963px;'title='7 threads\nduration:0.055\nstart:2017-03-24 08:02:43\nend:2017-03-24 08:02:46'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:995.724px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:02:46\nend:2017-03-24 08:02:46'></div><div class='bar' style='background-color:#03969D;height:57.983px;width:140.000px;left:420px; top:995.748px;'title='7 threads\nduration:1.200\nstart:2017-03-24 08:02:46\nend:2017-03-24 08:03:58'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:1055.732px;'title='8 threads\nduration:0.043\nstart:2017-03-24 08:03:58\nend:2017-03-24 08:04:01'></div><div class='bar' style='background-color:#03969D;height:166.107px;width:140.000px;left:420px; top:1057.866px;'title='7 threads\nduration:3.362\nstart:2017-03-24 08:04:01\nend:2017-03-24 08:07:23'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1225.973px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:07:23\nend:2017-03-24 08:07:23'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1225.996px;'title='7 threads\nduration:0.065\nstart:2017-03-24 08:07:23\nend:2017-03-24 08:07:26'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1229.262px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:07:26\nend:2017-03-24 08:07:26'></div><div class='bar' style='background-color:#03969D;height:23.702px;width:140.000px;left:420px; top:1229.286px;'title='7 threads\nduration:0.514\nstart:2017-03-24 08:07:26\nend:2017-03-24 08:07:57'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1254.988px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:07:57\nend:2017-03-24 08:07:57'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1255.012px;'title='7 threads\nduration:0.071\nstart:2017-03-24 08:07:57\nend:2017-03-24 08:08:02'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1258.578px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:08:02\nend:2017-03-24 08:08:02'></div><div class='bar' style='background-color:#03969D;height:14.458px;width:140.000px;left:420px; top:1258.601px;'title='7 threads\nduration:0.329\nstart:2017-03-24 08:08:02\nend:2017-03-24 08:08:21'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1275.059px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:08:21\nend:2017-03-24 08:08:21'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1275.083px;'title='7 threads\nduration:0.078\nstart:2017-03-24 08:08:21\nend:2017-03-24 08:08:26'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1278.982px;'title='6 threads\nduration:0.001\nstart:2017-03-24 08:08:26\nend:2017-03-24 08:08:26'></div><div class='bar' style='background-color:#03969D;height:39.043px;width:140.000px;left:420px; top:1279.009px;'title='7 threads\nduration:0.821\nstart:2017-03-24 08:08:26\nend:2017-03-24 08:09:15'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1320.052px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:09:15\nend:2017-03-24 08:09:15'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1320.076px;'title='7 threads\nduration:0.071\nstart:2017-03-24 08:09:15\nend:2017-03-24 08:09:20'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1323.641px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:09:20\nend:2017-03-24 08:09:20'></div><div class='bar' style='background-color:#03969D;height:6.894px;width:140.000px;left:420px; top:1323.665px;'title='7 threads\nduration:0.178\nstart:2017-03-24 08:09:20\nend:2017-03-24 08:09:30'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1332.559px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:09:30\nend:2017-03-24 08:09:30'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1332.583px;'title='7 threads\nduration:0.002\nstart:2017-03-24 08:09:30\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:1332.678px;'title='8 threads\nduration:0.008\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:31'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:180.000px;left:420px; top:1333.076px;'title='9 threads\nduration:0.044\nstart:2017-03-24 08:09:31\nend:2017-03-24 08:09:34'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:1335.253px;'title='8 threads\nduration:0.000\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:34'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:180.000px;left:420px; top:1335.275px;'title='9 threads\nduration:0.024\nstart:2017-03-24 08:09:34\nend:2017-03-24 08:09:35'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:1336.458px;'title='8 threads\nduration:0.033\nstart:2017-03-24 08:09:35\nend:2017-03-24 08:09:37'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1338.113px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:09:37\nend:2017-03-24 08:09:37'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:1338.137px;'title='8 threads\nduration:0.037\nstart:2017-03-24 08:09:37\nend:2017-03-24 08:09:39'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1339.970px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:09:39\nend:2017-03-24 08:09:39'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:1339.994px;'title='8 threads\nduration:0.012\nstart:2017-03-24 08:09:39\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1340.617px;'title='7 threads\nduration:0.000\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:1340.641px;'title='8 threads\nduration:0.006\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:40'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1340.921px;'title='7 threads\nduration:0.006\nstart:2017-03-24 08:09:40\nend:2017-03-24 08:09:41'></div><div class='bar' style='background-color:#03969D;height:186.646px;width:120.000px;left:420px; top:1341.231px;'title='6 threads\nduration:3.773\nstart:2017-03-24 08:09:41\nend:2017-03-24 08:13:27'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1529.877px;'title='7 threads\nduration:0.019\nstart:2017-03-24 08:13:27\nend:2017-03-24 08:13:28'></div><div class='bar' style='background-color:#03969D;height:82.741px;width:120.000px;left:420px; top:1530.829px;'title='6 threads\nduration:1.695\nstart:2017-03-24 08:13:28\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:1615.569px;'title='5 threads\nduration:0.001\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1615.639px;'title='6 threads\nduration:0.002\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1615.740px;'title='7 threads\nduration:0.001\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:10'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:160.000px;left:420px; top:1615.788px;'title='8 threads\nduration:0.049\nstart:2017-03-24 08:15:10\nend:2017-03-24 08:15:13'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1618.234px;'title='7 threads\nduration:0.043\nstart:2017-03-24 08:15:13\nend:2017-03-24 08:15:16'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1620.376px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:15:16\nend:2017-03-24 08:15:16'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1620.401px;'title='7 threads\nduration:0.033\nstart:2017-03-24 08:15:16\nend:2017-03-24 08:15:18'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1622.072px;'title='6 threads\nduration:0.000\nstart:2017-03-24 08:15:18\nend:2017-03-24 08:15:18'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1622.095px;'title='7 threads\nduration:0.061\nstart:2017-03-24 08:15:18\nend:2017-03-24 08:15:22'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1625.151px;'title='6 threads\nduration:0.003\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:22'></div><div class='bar' style='background-color:#03969D;height:6.057px;width:100.000px;left:420px; top:1625.319px;'title='5 threads\nduration:0.161\nstart:2017-03-24 08:15:22\nend:2017-03-24 08:15:31'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1633.376px;'title='6 threads\nduration:0.012\nstart:2017-03-24 08:15:31\nend:2017-03-24 08:15:32'></div><div class='bar' style='background-color:#03969D;height:46.993px;width:100.000px;left:420px; top:1633.997px;'title='5 threads\nduration:0.980\nstart:2017-03-24 08:15:32\nend:2017-03-24 08:16:31'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1682.990px;'title='6 threads\nduration:0.013\nstart:2017-03-24 08:16:31\nend:2017-03-24 08:16:32'></div><div class='bar' style='background-color:#03969D;height:41.295px;width:100.000px;left:420px; top:1683.647px;'title='5 threads\nduration:0.866\nstart:2017-03-24 08:16:32\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:80.000px;left:420px; top:1726.943px;'title='4 threads\nduration:0.001\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:1727.009px;'title='5 threads\nduration:0.001\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1727.056px;'title='6 threads\nduration:0.002\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:24'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1727.158px;'title='7 threads\nduration:0.056\nstart:2017-03-24 08:17:24\nend:2017-03-24 08:17:27'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1729.936px;'title='6 threads\nduration:0.016\nstart:2017-03-24 08:17:27\nend:2017-03-24 08:17:28'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:140.000px;left:420px; top:1730.739px;'title='7 threads\nduration:0.029\nstart:2017-03-24 08:17:28\nend:2017-03-24 08:17:30'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1732.197px;'title='6 threads\nduration:0.039\nstart:2017-03-24 08:17:30\nend:2017-03-24 08:17:32'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:1734.147px;'title='5 threads\nduration:0.026\nstart:2017-03-24 08:17:32\nend:2017-03-24 08:17:34'></div><div class='bar' style='background-color:#03969D;height:4.872px;width:80.000px;left:420px; top:1735.455px;'title='4 threads\nduration:0.137\nstart:2017-03-24 08:17:34\nend:2017-03-24 08:17:42'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:1742.326px;'title='5 threads\nduration:0.012\nstart:2017-03-24 08:17:42\nend:2017-03-24 08:17:43'></div><div class='bar' style='background-color:#03969D;height:52.644px;width:80.000px;left:420px; top:1742.937px;'title='4 threads\nduration:1.093\nstart:2017-03-24 08:17:43\nend:2017-03-24 08:18:48'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:1797.580px;'title='5 threads\nduration:0.026\nstart:2017-03-24 08:18:48\nend:2017-03-24 08:18:50'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:1798.882px;'title='6 threads\nduration:0.013\nstart:2017-03-24 08:18:50\nend:2017-03-24 08:18:51'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:1799.544px;'title='5 threads\nduration:0.026\nstart:2017-03-24 08:18:51\nend:2017-03-24 08:18:52'></div><div class='bar' style='background-color:#03969D;height:76.628px;width:80.000px;left:420px; top:1800.865px;'title='4 threads\nduration:1.573\nstart:2017-03-24 08:18:52\nend:2017-03-24 08:20:27'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:1879.493px;'title='5 threads\nduration:0.018\nstart:2017-03-24 08:20:27\nend:2017-03-24 08:20:28'></div><div class='bar' style='background-color:#03969D;height:46.978px;width:80.000px;left:420px; top:1880.405px;'title='4 threads\nduration:0.980\nstart:2017-03-24 08:20:28\nend:2017-03-24 08:21:27'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:1929.383px;'title='5 threads\nduration:0.018\nstart:2017-03-24 08:21:27\nend:2017-03-24 08:21:28'></div><div class='bar' style='background-color:#03969D;height:143.166px;width:80.000px;left:420px; top:1930.296px;'title='4 threads\nduration:2.903\nstart:2017-03-24 08:21:28\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2075.462px;'title='3 threads\nduration:0.000\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:80.000px;left:420px; top:2075.486px;'title='4 threads\nduration:0.002\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:2075.583px;'title='5 threads\nduration:0.001\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:22'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:2075.628px;'title='6 threads\nduration:0.067\nstart:2017-03-24 08:24:22\nend:2017-03-24 08:24:26'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:2078.958px;'title='5 threads\nduration:0.001\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:26'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:2079.029px;'title='6 threads\nduration:0.046\nstart:2017-03-24 08:24:26\nend:2017-03-24 08:24:29'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:2081.346px;'title='5 threads\nduration:0.044\nstart:2017-03-24 08:24:29\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:80.000px;left:420px; top:2083.542px;'title='4 threads\nduration:0.002\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:2083.651px;'title='5 threads\nduration:0.002\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:2083.736px;'title='6 threads\nduration:0.003\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:2083.888px;'title='5 threads\nduration:0.000\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:32'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:120.000px;left:420px; top:2083.912px;'title='6 threads\nduration:0.009\nstart:2017-03-24 08:24:32\nend:2017-03-24 08:24:33'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:2084.363px;'title='5 threads\nduration:0.004\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:33'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:80.000px;left:420px; top:2084.538px;'title='4 threads\nduration:0.057\nstart:2017-03-24 08:24:33\nend:2017-03-24 08:24:36'></div><div class='bar' style='background-color:#03969D;height:109.211px;width:60.000px;left:420px; top:2087.372px;'title='3 threads\nduration:2.224\nstart:2017-03-24 08:24:36\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2198.583px;'title='2 threads\nduration:0.000\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2198.606px;'title='3 threads\nduration:0.001\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:80.000px;left:420px; top:2198.651px;'title='4 threads\nduration:0.003\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:50'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:2198.799px;'title='5 threads\nduration:0.068\nstart:2017-03-24 08:26:50\nend:2017-03-24 08:26:54'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:80.000px;left:420px; top:2202.181px;'title='4 threads\nduration:0.003\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:26:54'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:100.000px;left:420px; top:2202.320px;'title='5 threads\nduration:0.058\nstart:2017-03-24 08:26:54\nend:2017-03-24 08:26:58'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:80.000px;left:420px; top:2205.216px;'title='4 threads\nduration:0.041\nstart:2017-03-24 08:26:58\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2207.259px;'title='3 threads\nduration:0.004\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2207.452px;'title='2 threads\nduration:0.001\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:00'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2207.480px;'title='3 threads\nduration:0.003\nstart:2017-03-24 08:27:00\nend:2017-03-24 08:27:01'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:80.000px;left:420px; top:2207.647px;'title='4 threads\nduration:0.012\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:01'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2208.265px;'title='3 threads\nduration:0.064\nstart:2017-03-24 08:27:01\nend:2017-03-24 08:27:05'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2211.480px;'title='2 threads\nduration:0.000\nstart:2017-03-24 08:27:05\nend:2017-03-24 08:27:05'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2211.503px;'title='3 threads\nduration:0.013\nstart:2017-03-24 08:27:05\nend:2017-03-24 08:27:06'></div><div class='bar' style='background-color:#03969D;height:49.190px;width:40.000px;left:420px; top:2212.128px;'title='2 threads\nduration:1.024\nstart:2017-03-24 08:27:06\nend:2017-03-24 08:28:07'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2263.318px;'title='1 threads\nduration:0.001\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:07'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2263.390px;'title='2 threads\nduration:0.008\nstart:2017-03-24 08:28:07\nend:2017-03-24 08:28:08'></div><div class='bar' style='background-color:#03969D;height:4.411px;width:60.000px;left:420px; top:2263.791px;'title='3 threads\nduration:0.128\nstart:2017-03-24 08:28:08\nend:2017-03-24 08:28:16'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2270.202px;'title='2 threads\nduration:0.000\nstart:2017-03-24 08:28:16\nend:2017-03-24 08:28:16'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2270.226px;'title='3 threads\nduration:0.039\nstart:2017-03-24 08:28:16\nend:2017-03-24 08:28:18'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2272.158px;'title='2 threads\nduration:0.000\nstart:2017-03-24 08:28:18\nend:2017-03-24 08:28:18'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:60.000px;left:420px; top:2272.178px;'title='3 threads\nduration:0.043\nstart:2017-03-24 08:28:18\nend:2017-03-24 08:28:21'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2274.351px;'title='2 threads\nduration:0.036\nstart:2017-03-24 08:28:21\nend:2017-03-24 08:28:23'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2276.134px;'title='1 threads\nduration:0.026\nstart:2017-03-24 08:28:23\nend:2017-03-24 08:28:24'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2277.419px;'title='2 threads\nduration:0.098\nstart:2017-03-24 08:28:24\nend:2017-03-24 08:28:30'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2282.339px;'title='1 threads\nduration:0.001\nstart:2017-03-24 08:28:30\nend:2017-03-24 08:28:30'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2282.367px;'title='2 threads\nduration:0.013\nstart:2017-03-24 08:28:30\nend:2017-03-24 08:28:31'></div><div class='bar' style='background-color:#03969D;height:14.715px;width:20.000px;left:420px; top:2282.996px;'title='1 threads\nduration:0.334\nstart:2017-03-24 08:28:31\nend:2017-03-24 08:28:51'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2299.711px;'title='2 threads\nduration:0.017\nstart:2017-03-24 08:28:51\nend:2017-03-24 08:28:52'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2300.546px;'title='1 threads\nduration:0.046\nstart:2017-03-24 08:28:52\nend:2017-03-24 08:28:55'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:0.000px;left:420px; top:2302.841px;'title='0 threads\nduration:0.019\nstart:2017-03-24 08:28:55\nend:2017-03-24 08:28:56'></div><div class='bar' style='background-color:#03969D;height:7.516px;width:20.000px;left:420px; top:2303.779px;'title='1 threads\nduration:0.190\nstart:2017-03-24 08:28:56\nend:2017-03-24 08:29:07'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:40.000px;left:420px; top:2313.294px;'title='2 threads\nduration:0.007\nstart:2017-03-24 08:29:07\nend:2017-03-24 08:29:08'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2313.639px;'title='1 threads\nduration:0.071\nstart:2017-03-24 08:29:08\nend:2017-03-24 08:29:12'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:0.000px;left:420px; top:2317.191px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:29:12\nend:2017-03-24 08:29:12'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2317.214px;'title='1 threads\nduration:0.085\nstart:2017-03-24 08:29:12\nend:2017-03-24 08:29:17'></div><div class='bar' style='background-color:#03969D;height:8.079px;width:0.000px;left:420px; top:2321.486px;'title='0 threads\nduration:0.202\nstart:2017-03-24 08:29:17\nend:2017-03-24 08:29:29'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2331.565px;'title='1 threads\nduration:0.013\nstart:2017-03-24 08:29:29\nend:2017-03-24 08:29:30'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:0.000px;left:420px; top:2332.229px;'title='0 threads\nduration:0.000\nstart:2017-03-24 08:29:30\nend:2017-03-24 08:29:30'></div><div class='bar' style='background-color:#03969D;height:3.033px;width:20.000px;left:420px; top:2332.251px;'title='1 threads\nduration:0.101\nstart:2017-03-24 08:29:30\nend:2017-03-24 08:29:36'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:0.000px;left:420px; top:2337.283px;'title='0 threads\nduration:0.001\nstart:2017-03-24 08:29:36\nend:2017-03-24 08:29:36'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2337.312px;'title='1 threads\nduration:0.013\nstart:2017-03-24 08:29:36\nend:2017-03-24 08:29:37'></div><div class='bar' style='background-color:#03969D;height:29.516px;width:0.000px;left:420px; top:2337.965px;'title='0 threads\nduration:0.630\nstart:2017-03-24 08:29:37\nend:2017-03-24 08:30:15'></div><div class='bar' style='background-color:#03969D;height:4.981px;width:20.000px;left:420px; top:2369.482px;'title='1 threads\nduration:0.140\nstart:2017-03-24 08:30:15\nend:2017-03-24 08:30:23'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:0.000px;left:420px; top:2376.462px;'title='0 threads\nduration:0.059\nstart:2017-03-24 08:30:23\nend:2017-03-24 08:30:27'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2379.416px;'title='1 threads\nduration:0.019\nstart:2017-03-24 08:30:27\nend:2017-03-24 08:30:28'></div><div class='bar' style='background-color:#03969D;height:21.926px;width:0.000px;left:420px; top:2380.374px;'title='0 threads\nduration:0.479\nstart:2017-03-24 08:30:28\nend:2017-03-24 08:30:57'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2404.300px;'title='1 threads\nduration:0.019\nstart:2017-03-24 08:30:57\nend:2017-03-24 08:30:58'></div><div class='bar' style='background-color:#03969D;height:67.367px;width:0.000px;left:420px; top:2405.252px;'title='0 threads\nduration:1.387\nstart:2017-03-24 08:30:58\nend:2017-03-24 08:32:21'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2474.619px;'title='1 threads\nduration:0.024\nstart:2017-03-24 08:32:21\nend:2017-03-24 08:32:22'></div><div class='bar' style='background-color:#03969D;height:82.439px;width:0.000px;left:420px; top:2475.825px;'title='0 threads\nduration:1.689\nstart:2017-03-24 08:32:22\nend:2017-03-24 08:34:04'></div><div class='bar' style='background-color:#03969D;height:3.000px;width:20.000px;left:420px; top:2560.264px;'title='1 threads\nduration:0.019\nstart:2017-03-24 08:34:04\nend:2017-03-24 08:34:05'></div><div class='bar' style='background-color:#03969D;height:12.231px;width:0.000px;left:420px; top:2561.217px;'title='0 threads\nduration:0.285\nstart:2017-03-24 08:34:05\nend:2017-03-24 08:34:22'></div>\n        </div>\n    </body>"
  },
  {
    "path": "docs/source/_static/example_funcreport.html",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<meta name=\"generator\" content=\"NiReports: https://www.nipreps.org/\" />\n<title>sub-03286_ses-NOT1ACH001_task-rest_bold :: MRIQC's BOLD fMRI report</title>\n<script src=\"https://code.jquery.com/jquery-3.6.0.min.js\" integrity=\"sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=\" crossorigin=\"anonymous\"></script>\n<link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65\" crossorigin=\"anonymous\">\n<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js\" integrity=\"sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4\" crossorigin=\"anonymous\"></script>\n\n<script>\nvar timestamp = Date.now()\n\nfunction read_form() {\n    var ds = \"<unset>\";\n    var sub = \"sub-03286_ses-NOT1ACH001_task-rest_bold.nii.gz\";\n\n    var artifacts = [];\n    $('#qcartifacts-group input:checked').each(function() {\n        artifacts.push($(this).attr('name'));\n    });\n\n    var rating = $('#qcslider').val();\n    var payload = {\n        'dataset': ds,\n        'subject': sub,\n        'rating': rating,\n        'artifacts': artifacts,\n        'time_sec': (Date.now() - timestamp) / 1000,\n        'confidence': $('#qcextra-confidence').val(),\n        'comments': $('#qcextra-comments').val()\n    };\n\n    var file = new Blob([JSON.stringify(payload)], {type: 'text/json'});\n    $('#btn-download').attr('href', URL.createObjectURL(file));\n    $('#btn-download').attr('download', payload['dataset'] + \"_\" + payload['subject'] + \".json\");\n    return payload\n};\n\nfunction toggle_rating() {\n    if ($('#qcrating-menu').hasClass('d-none')) {\n        $('#qcrating-menu').removeClass('d-none');\n        $('#qcrating-toggler').prop('checked', true);\n    } else {\n        $('#qcrating-menu').addClass('d-none');\n        $('#qcrating-toggler').prop('checked', false);\n    }\n};\n\n$(window).on('load',function(){\n    var authorization = $('#btn-post').val()\n    if (authorization.includes(\"secret_token\")) {\n        $('#btn-post').addClass('d-none');\n    };\n    timestamp = Date.now();\n});\n\n</script>\n<style type=\"text/css\">\n/* The slider itself */\n.slider {\n  -webkit-appearance: none;  /* Override default CSS styles */\n  appearance: none;\n  margin-bottom: 8px;\n  margin-left: 10%;\n  width: 80%;\n  height: 5px; /* Specified height */\n  background: #d3d3d3; /* Grey background */\n  outline: none; /* Remove outline */\n  opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */\n  -webkit-transition: .2s; /* 0.2 seconds transition on hover */\n  transition: opacity .2s;\n}\n\n/* Mouse-over effects */\n.slider:hover {\n  opacity: 1; /* Fully shown on mouse-over */\n}\n\n.slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 25px;\n  height: 25px;\n  border: 0;\n  background: url('https://raw.githubusercontent.com/nipreps/nireports/main/assets/slider-handle.png');\n  cursor: pointer;\n  z-index: 2000 !important;\n}\n\n.slider::-moz-range-thumb {\n  width: 25px;\n  height: 25px;\n  border: 0;\n  background: url('https://raw.githubusercontent.com/nipreps/nireports/main/assets/slider-handle.png');\n  cursor: pointer;\n  z-index: 2000 !important;\n}\n\n</style>\n</head>\n<body style=\"font-family: helvetica;\">\n<nav class=\"navbar fixed-top navbar-expand-lg bg-light\">\n<div class=\"container-fluid\">\n<div class=\"collapse navbar-collapse\" id=\"navbarSupportedContent\">\n    <ul class=\"navbar-nav me-auto mb-2 mb-lg-0\">\n        <li class=\"nav-item\"><a class=\"nav-link\" href=\"#Basic echo-wise reports\">Basic echo-wise reports</a></li>\n        <li class=\"nav-item\"><a class=\"nav-link\" href=\"#Extended echo-wise reports\">Extended echo-wise reports</a></li>\n        <li class=\"nav-item dropdown\">\n            <a class=\"nav-link dropdown-toggle\" id=\"navbarAbout\" role=\"button\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\" href=\"#About\">\n            About\n            </a>\n            <ul class=\"dropdown-menu\">\n                <li><a class=\"dropdown-item\" href=\"#errors\">Errors</a></li>\n                <li><a class=\"dropdown-item\" href=\"#meta-about-metadata\">Reproducibility and provenance information</a></li>\n            </ul>\n        </li>\n    </ul>\n</div>\n</div>\n<div class=\"d-flex flex-row-reverse\">\n<div class=\"form-check form-switch align-self-center flex-fill me-4\">\n<input class=\"form-check-input\" type=\"checkbox\" id=\"qcrating-toggler\"></input>\n<label class=\"form-check-label\" style=\"width: 100pt;\" for=\"qcrating-toggler\">Rating widget</label>\n</div>\n</div>\n</nav>\n<noscript>\n    <h1 class=\"text-danger\"> The navigation menu uses Javascript. Without it this report might not work as expected </h1>\n</noscript>\n\n    <div id=\"Basic echo-wise reports\" class=\"mt-5\">\n    <h1 class=\"sub-report-title pt-5 ps-4\">Basic echo-wise reports</h1>\n        <div id=\"datatype-figures_desc-stdev_session-NOT1ACH001_subject-03286_suffix-bold_task-rest\" class=\"ps-4 pe-4 mb-2\">\n<h3 class=\"run-title mt-3\">Standard deviation of signal through time</h3><p class=\"elem-caption\">The voxel-wise standard deviation of the signal (variability along time).</p>                    <div class=\"reportlet\">\n<img class=\"svg-reportlet\" src=\"./sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-stdev_bold.svg\" style=\"width: 100%\" />\n</div>\n<small>Get figure file: <a href=\"./sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-stdev_bold.svg\" target=\"_blank\">sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-stdev_bold.svg</a></small>\n\n        </div>\n        <div id=\"datatype-figures_desc-background_session-NOT1ACH001_subject-03286_suffix-bold_task-rest\" class=\"ps-4 pe-4 mb-2\">\n<h3 class=\"run-title mt-3\">View of the background of the voxel-wise average of the BOLD timeseries</h3><p class=\"elem-caption\">This panel shows a mosaic enhancing the background around the head. Artifacts usually unveil themselves in the air surrounding the head, where no signal sources are present.</p>                    <div class=\"reportlet\">\n<img class=\"svg-reportlet\" src=\"./sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-background_bold.svg\" style=\"width: 100%\" />\n</div>\n<small>Get figure file: <a href=\"./sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-background_bold.svg\" target=\"_blank\">sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-background_bold.svg</a></small>\n\n        </div>\n        <div id=\"datatype-figures_desc-zoomed_session-NOT1ACH001_subject-03286_suffix-bold_task-rest\" class=\"ps-4 pe-4 mb-2\">\n<h3 class=\"run-title mt-3\">Voxel-wise average of BOLD time-series, zoomed-in covering just the brain</h3><p class=\"elem-caption\">This panel shows a mosaic of the brain. This mosaic is the most suitable to screen head-motion intensity inhomogeneities, global/local noise, signal leakage (for example, from the eyeballs and across the phase-encoding axis), etc.</p>                    <div class=\"reportlet\">\n<img class=\"svg-reportlet\" src=\"./sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-zoomed_bold.svg\" style=\"width: 100%\" />\n</div>\n<small>Get figure file: <a href=\"./sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-zoomed_bold.svg\" target=\"_blank\">sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-zoomed_bold.svg</a></small>\n\n        </div>\n        <div id=\"datatype-figures_desc-carpet_session-NOT1ACH001_subject-03286_suffix-bold_task-rest\" class=\"ps-4 pe-4 mb-2\">\n<h3 class=\"run-title mt-3\">Carpetplot and nuisance signals</h3><p class=\"elem-caption\">The so-called &laquo;carpetplot&raquo; may assist in assessing head-motion derived artifacts and respiation effects.</p>                    <div class=\"reportlet\">\n<img class=\"svg-reportlet\" src=\"./sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-carpet_bold.svg\" style=\"width: 100%\" />\n</div>\n<small>Get figure file: <a href=\"./sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-carpet_bold.svg\" target=\"_blank\">sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-carpet_bold.svg</a></small>\n\n        </div>\n    </div>\n    <div id=\"Extended echo-wise reports\" class=\"mt-5\">\n    <h1 class=\"sub-report-title pt-5 ps-4\">Extended echo-wise reports</h1>\n        <div id=\"datatype-figures_desc-mean_session-NOT1ACH001_subject-03286_suffix-bold_task-rest\" class=\"ps-4 pe-4 mb-2\">\n<h3 class=\"run-title mt-3\">Voxel-wise average of BOLD time-series</h3><p class=\"elem-caption\">The average signal calculated across the last axis (time).</p>                    <div class=\"reportlet\">\n<img class=\"svg-reportlet\" src=\"./sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-mean_bold.svg\" style=\"width: 100%\" />\n</div>\n<small>Get figure file: <a href=\"./sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-mean_bold.svg\" target=\"_blank\">sub-03286/figures/sub-03286_ses-NOT1ACH001_task-rest_desc-mean_bold.svg</a></small>\n\n        </div>\n    </div>\n    <div id=\"About\" class=\"mt-5\">\n    <h1 class=\"sub-report-title pt-5 ps-4\">About</h1>\n        <div id=\"errors\" class=\"ps-4 pe-4 mb-2\">\n<h2 class=\"sub-report-group mt-4\">Errors</h2>                    <p class=\"alert alert-success\" role=\"alert\">No errors to report!</p>\n        </div>\n        <div id=\"meta-about-metadata\" class=\"ps-4 pe-4 mb-2\">\n<h2 class=\"sub-report-group mt-4\">Reproducibility and provenance information</h2><p class=\"elem-caption\">Thanks for using <em>MRIQC</em>. The following information may assist in\nreconstructing the provenance of the corresponding derivatives.\n</p>                    <div class=\"accordion accordion-flush\" id=\"about-metadata\">\n\n\n  <div class=\"accordion-item\">\n    <h2 class=\"accordion-header\" id=\"about-metadata-0\">\n      <button class=\"accordion-button collapsed\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#about-metadata-0-collapse\" aria-controls=\"about-metadata-0-collapse\">\n        Provenance Information\n      </button>\n    </h2>\n    <div id=\"about-metadata-0-collapse\" class=\"accordion-collapse collapse\" aria-labelledby=\"about-metadata-0-heading\" data-bs-parent=\"#about-metadata-0\">\n      <div class=\"accordion-body metadata-table\">\n      <table id=\"about-metadata-table-0\" class=\"table table-sm table-striped\">\n<tr><td colspan=2>Execution environment</td><td>docker</td></tr>\n<tr><td colspan=2>Input filename</td><td><BIDS root>/sub-03286/ses-NOT1ACH001/func/sub-03286_ses-NOT1ACH001_task-rest_bold.nii.gz</td></tr>\n<tr><td>Versions</td><td>MRIQC</td><td>24.1.0.dev0+gd5b13cb5.d20240826</td></tr>\n<tr><td>Versions</td><td>NiPype</td><td>1.8.6</td></tr>\n<tr><td>Versions</td><td>TemplateFlow</td><td>24.2.0</td></tr>\n<tr><td colspan=2>md5sum</td><td>6462858509954133c8448a10b04757e5</td></tr>\n</table>\n\n      </div>\n    </div>\n  </div>\n\n\n  <div class=\"accordion-item\">\n    <h2 class=\"accordion-header\" id=\"about-metadata-1\">\n      <button class=\"accordion-button collapsed\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#about-metadata-1-collapse\" aria-controls=\"about-metadata-1-collapse\">\n        Dataset Information\n      </button>\n    </h2>\n    <div id=\"about-metadata-1-collapse\" class=\"accordion-collapse collapse\" aria-labelledby=\"about-metadata-1-heading\" data-bs-parent=\"#about-metadata-1\">\n      <div class=\"accordion-body metadata-table\">\n      <table id=\"about-metadata-table-1\" class=\"table table-sm table-striped\">\n<tr><td>AcquisitionMatrixPE</td><td>95</td></tr>\n<tr><td>AcquisitionNumber</td><td>8</td></tr>\n<tr><td>AcquisitionTime</td><td>10:15:56.720000</td></tr>\n<tr><td>B0FieldSource</td><td>pepolar_fmap0</td></tr>\n<tr><td>BodyPartExamined</td><td>BRAIN</td></tr>\n<tr><td>CoilString</td><td>MULTI COIL</td></tr>\n<tr><td>ConversionSoftware</td><td>dcm2niix</td></tr>\n<tr><td>ConversionSoftwareVersion</td><td>v1.0.20211006</td></tr>\n<tr><td>DeviceSerialNumber</td><td>17057</td></tr>\n<tr><td>EchoTime</td><td>0.039</td></tr>\n<tr><td>EchoTrainLength</td><td>95</td></tr>\n<tr><td>EffectiveEchoSpacing</td><td>0.000608401</td></tr>\n<tr><td>EstimatedEffectiveEchoSpacing</td><td>0.000608401</td></tr>\n<tr><td>EstimatedTotalReadoutTime</td><td>0.0577981</td></tr>\n<tr><td>FlipAngle</td><td>65</td></tr>\n<tr><td>ImageOrientationPatientDICOM</td><td>[0.998569, 0.00766079, 0.0529228, -0.00367802, 0.997181, -0.0749475]</td></tr>\n<tr><td>ImageType</td><td>['ORIGINAL', 'PRIMARY', 'M', 'FFE', 'M', 'FFE']</td></tr>\n<tr><td>ImagingFrequency</td><td>127.804</td></tr>\n<tr><td>InPlanePhaseEncodingDirectionDICOM</td><td>COL</td></tr>\n<tr><td>InstitutionName</td><td>SPMMRC Nottingham</td></tr>\n<tr><td>InstitutionalDepartmentName</td><td>MRI</td></tr>\n<tr><td>MRAcquisitionType</td><td>2D</td></tr>\n<tr><td>MTState</td><td>False</td></tr>\n<tr><td>MagneticFieldStrength</td><td>3</td></tr>\n<tr><td>Manufacturer</td><td>Philips</td></tr>\n<tr><td>ManufacturersModelName</td><td>Achieva</td></tr>\n<tr><td>Modality</td><td>MR</td></tr>\n<tr><td>ParallelAcquisitionTechnique</td><td>SENSE</td></tr>\n<tr><td>PartialFourierDirection</td><td>PHASE</td></tr>\n<tr><td>PartialFourierEnabled</td><td>YES</td></tr>\n<tr><td>PatientPosition</td><td>HFS</td></tr>\n<tr><td>PercentPhaseFOV</td><td>100</td></tr>\n<tr><td>PercentSampling</td><td>100</td></tr>\n<tr><td>PhaseEncodingAxis</td><td>j</td></tr>\n<tr><td>PhaseEncodingDirection</td><td>j</td></tr>\n<tr><td>PhaseEncodingStepsNoPartialFourier</td><td>95</td></tr>\n<tr><td>PhilipsRescaleIntercept</td><td>0</td></tr>\n<tr><td>PhilipsRescaleSlope</td><td>0.818071</td></tr>\n<tr><td>PhilipsScaleSlope</td><td>0.00153639</td></tr>\n<tr><td>PixelBandwidth</td><td>2328</td></tr>\n<tr><td>ProcedureStepDescription</td><td>H1408201447</td></tr>\n<tr><td>ProtocolName</td><td>WIP rs-fmri_singleMB4_HF0.8</td></tr>\n<tr><td>ReconMatrixPE</td><td>96</td></tr>\n<tr><td>RepetitionTime</td><td>1.15</td></tr>\n<tr><td>SAR</td><td>0.0227829</td></tr>\n<tr><td>ScanOptions</td><td>FS</td></tr>\n<tr><td>ScanningSequence</td><td>GR</td></tr>\n<tr><td>SequenceVariant</td><td>SK</td></tr>\n<tr><td>SeriesDescription</td><td>rs-fmri_singleMB4_HF0.8</td></tr>\n<tr><td>SeriesNumber</td><td>801</td></tr>\n<tr><td>SliceThickness</td><td>2.4</td></tr>\n<tr><td>SoftwareVersions</td><td>5.3.0\\5.3.0.3</td></tr>\n<tr><td>SpacingBetweenSlices</td><td>2.4</td></tr>\n<tr><td>StationName</td><td>PHILIPS-T6O3EHP</td></tr>\n<tr><td>TaskName</td><td>rest</td></tr>\n<tr><td>TotalReadoutTime</td><td>0.0577981</td></tr>\n<tr><td>UsePhilipsFloatNotDisplayScaling</td><td>1</td></tr>\n<tr><td>WaterFatShift</td><td>25.3797</td></tr>\n</table>\n\n      </div>\n    </div>\n  </div>\n\n\n  <div class=\"accordion-item\">\n    <h2 class=\"accordion-header\" id=\"about-metadata-2\">\n      <button class=\"accordion-button collapsed\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#about-metadata-2-collapse\" aria-controls=\"about-metadata-2-collapse\">\n        Extracted Image quality metrics (IQMs)\n      </button>\n    </h2>\n    <div id=\"about-metadata-2-collapse\" class=\"accordion-collapse collapse\" aria-labelledby=\"about-metadata-2-heading\" data-bs-parent=\"#about-metadata-2\">\n      <div class=\"accordion-body metadata-table\">\n      <table id=\"about-metadata-table-2\" class=\"table table-sm table-striped\">\n<tr><td colspan=3>aor</td><td>0.0037614999999999997</td></tr>\n<tr><td colspan=3>aqi</td><td>0.023297618900000004</td></tr>\n<tr><td colspan=3>dummy_trs</td><td>0</td></tr>\n<tr><td>dvars</td><td colspan=2>nstd</td><td>44.00571358506266</td></tr>\n<tr><td>dvars</td><td colspan=2>std</td><td>1.1092985154385975</td></tr>\n<tr><td>dvars</td><td colspan=2>vstd</td><td>0.9788209838095238</td></tr>\n<tr><td colspan=3>efc</td><td>0.4504</td></tr>\n<tr><td colspan=3>fber</td><td>553307.625</td></tr>\n<tr><td>fd</td><td colspan=2>mean</td><td>0.2239716689042766</td></tr>\n<tr><td>fd</td><td colspan=2>num</td><td>215</td></tr>\n<tr><td>fd</td><td colspan=2>perc</td><td>53.75</td></tr>\n<tr><td>fwhm</td><td colspan=2>avg</td><td>2.3961225784666915</td></tr>\n<tr><td>fwhm</td><td colspan=2>x</td><td>2.4589816467849612</td></tr>\n<tr><td>fwhm</td><td colspan=2>y</td><td>2.401594514446574</td></tr>\n<tr><td>fwhm</td><td colspan=2>z</td><td>2.32779157416854</td></tr>\n<tr><td colspan=3>gcor</td><td>0.00260741</td></tr>\n<tr><td>gsr</td><td colspan=2>x</td><td>-0.018688196316361427</td></tr>\n<tr><td>gsr</td><td colspan=2>y</td><td>0.006971913389861584</td></tr>\n<tr><td>size</td><td colspan=2>t</td><td>400</td></tr>\n<tr><td>size</td><td colspan=2>x</td><td>96</td></tr>\n<tr><td>size</td><td colspan=2>y</td><td>96</td></tr>\n<tr><td>size</td><td colspan=2>z</td><td>48</td></tr>\n<tr><td colspan=3>snr</td><td>2.602224857820378</td></tr>\n<tr><td>spacing</td><td colspan=2>tr</td><td>1.1500004529953003</td></tr>\n<tr><td>spacing</td><td colspan=2>x</td><td>2.3958332538604736</td></tr>\n<tr><td>spacing</td><td colspan=2>y</td><td>2.3958332538604736</td></tr>\n<tr><td>spacing</td><td colspan=2>z</td><td>2.4000000953674316</td></tr>\n<tr><td>summary</td><td>bg</td><td>k</td><td>93.4153</td></tr>\n<tr><td>summary</td><td>bg</td><td>mad</td><td>959.2436</td></tr>\n<tr><td>summary</td><td>bg</td><td>mean</td><td>17223.3027</td></tr>\n<tr><td>summary</td><td>bg</td><td>median</td><td>647.0</td></tr>\n<tr><td>summary</td><td>bg</td><td>n</td><td>339609.0</td></tr>\n<tr><td>summary</td><td>bg</td><td>p05</td><td>0.0</td></tr>\n<tr><td>summary</td><td>bg</td><td>p95</td><td>76174.0</td></tr>\n<tr><td>summary</td><td>bg</td><td>stdv</td><td>61176.0266</td></tr>\n<tr><td>summary</td><td>fg</td><td>k</td><td>2.1816</td></tr>\n<tr><td>summary</td><td>fg</td><td>mad</td><td>142208.0543</td></tr>\n<tr><td>summary</td><td>fg</td><td>mean</td><td>500750.8674</td></tr>\n<tr><td>summary</td><td>fg</td><td>median</td><td>481252.0</td></tr>\n<tr><td>summary</td><td>fg</td><td>n</td><td>102759.0</td></tr>\n<tr><td>summary</td><td>fg</td><td>p05</td><td>206803.0</td></tr>\n<tr><td>summary</td><td>fg</td><td>p95</td><td>833312.0</td></tr>\n<tr><td>summary</td><td>fg</td><td>stdv</td><td>184937.7685</td></tr>\n<tr><td colspan=3>tsnr</td><td>25.0640248588752</td></tr>\n</table>\n\n      </div>\n    </div>\n  </div>\n\n</div>\n        </div>\n    </div>\n\n<div id=\"qcrating-menu\" class=\"card position-fixed d-none\" style=\"width: 30%; top: 100px; left: 65%; max-height: 85%; overflow-y: auto;\">\n<div class=\"card-header m-0\">\n    Rating widget\n    <button type=\"button\" class=\"btn-close position-absolute top-0 end-0\" aria-label=\"Close\" id=\"close-qcrating-menu\" onclick=\"toggle_rating()\" style=\"margin: 10px 10px 0 0\"></button>\n</div>\n<div class=\"card-body\">\n<div class=\"accordion\">\n  <div class=\"accordion-item\">\n    <h2 class=\"accordion-header\" id=\"qcslider-head\">\n      <button class=\"accordion-button\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#qcslider-collapse\" aria-expanded=\"true\" aria-controls=\"qcslider-collapse\">Overall Quality Rating</button>\n    </h2>\n    <div id=\"qcslider-collapse\" class=\"accordion-collapse collapse show\" aria-labelledby=\"qcslider-head\">\n      <div class=\"accordion-body\">\n        <input type=\"range\" min=\"1.0\" max=\"4.0\" step=\"0.05\" value=\"2.5\" id=\"qcslider\" class=\"slider\">\n        <ul class=\"list-group list-group-horizontal slider-labels\" style=\"width: 100%\">\n            <li class=\"list-group-item list-group-item-danger small\" style=\"font-size: 0.7em; width: 25%; text-align:center\">Exclude</li>\n            <li class=\"list-group-item list-group-item-warning small\" style=\"font-size: 0.7em; width: 25%; text-align:center\">Poor</li>\n            <li class=\"list-group-item list-group-item-primary small\" style=\"font-size: 0.7em; width: 25%; text-align:center\">Acceptable</li>\n            <li class=\"list-group-item list-group-item-success small\" style=\"font-size: 0.7em; width: 25%; text-align:center\">Excellent</li>\n        </ul>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"accordion-item\">\n    <h2 class=\"accordion-header\" id=\"qcartifacts-head\">\n      <button class=\"accordion-button collapsed\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#qcartifacts-collapse\" aria-expanded=\"false\" aria-controls=\"qcartifacts-collapse\">\n        Record specific artifacts\n      </button>\n    </h2>\n    <div id=\"qcartifacts-collapse\" class=\"accordion-collapse collapse\" aria-labelledby=\"qcartifacts-head\">\n      <div class=\"accordion-body\">\n        <fieldset id=\"qcartifacts-group\" class=\"form-group\">\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"head-motion\" id=\"qcartifacts-item-0\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-0\">Head motion artifacts</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"eye-spillover\" id=\"qcartifacts-item-1\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-1\">Eye spillover through <abbr title=\"phase-encoding\">PE</abbr> axis</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"noneye-spillover\" id=\"qcartifacts-item-2\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-2\">Non-eye spillover through <abbr title=\"phase-encoding\">PE</abbr> axis</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"coil-failure\" id=\"qcartifacts-item-3\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-3\">Coil failure</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"noise-global\" id=\"qcartifacts-item-4\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-4\">Global noise</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"noise-local\" id=\"qcartifacts-item-5\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-5\">Local noise</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"em-perturbation\" id=\"qcartifacts-item-6\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-6\"><abbr title=\"electromagnetic\">EM</abbr> interference/perturbation</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"wrap-around\" id=\"qcartifacts-item-7\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-7\">Problematic <abbr title=\"field-of-view\">FoV</abbr> prescription / wrap-around</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"ghost-aliasing\" id=\"qcartifacts-item-8\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-8\">Aliasing ghosts</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"ghost-other\" id=\"qcartifacts-item-9\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-9\">Other ghosts (for example, <abbr title=\"radiofrequency\">RF</abbr> spoiling)</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"inu\" id=\"qcartifacts-item-10\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-10\">Intensity non-uniformity (B<sub>1</sub> bias)</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"field-variation\" id=\"qcartifacts-item-11\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-11\">Temporal B<sub>1</sub> field non-uniformity variation</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"processing\" id=\"qcartifacts-item-12\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-12\">Processing such as denoising, defacing or resamplings happened</label>\n            </div>\n            <div class=\"form-check form-switch small\">\n                <input class=\"form-check-input\" type=\"checkbox\" name=\"uncategorized\" id=\"qcartifacts-item-13\" />\n                <label class=\"form-check-label\" for=\"qcartifacts-item-13\">Other uncategorized artifact(s)</label>\n            </div>\n        </fieldset>\n      </div> <!-- accordion-body -->\n    </div> <!-- accordion-collapse -->\n  </div> <!-- accordion-item -->\n  <div class=\"accordion-item\">\n    <h2 class=\"accordion-header\" id=\"qcextra-head\">\n      <button class=\"accordion-button collapsed\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#qcextra-collapse\" aria-expanded=\"false\" aria-controls=\"qcextra-collapse\">\n        Extra details\n      </button>\n    </h2>\n    <div id=\"qcextra-collapse\" class=\"accordion-collapse collapse\" aria-labelledby=\"qcextra-head\">\n      <div class=\"accordion-body\">\n        <div class=\"input-group\">\n          <span class=\"input-group-text\">Comments</span>\n          <textarea class=\"form-control\" aria-label=\"Comments\" id=\"qcextra-comments\"></textarea>\n        </div>\n\n        <p style=\"margin-top: 20px; font-weight: bold\">Rater confidence</p>\n        <input type=\"range\" min=\"0.0\" max=\"4.0\" step=\"0.05\" value=\"3.5\" id=\"qcextra-confidence\" class=\"slider\" style=\"margin-left: 22%;width: 56%;\">\n        <ul class=\"list-group list-group-horizontal slider-labels\" style=\"width: 100%\">\n            <li class=\"list-group-item list-group-item-warning small\" style=\"width: 50%; text-align:center\">Doubtful</li>\n            <li class=\"list-group-item list-group-item-success bg-success text-white small\" style=\"width: 50%; text-align:center\">Confident</li>\n        </ul>\n       </div> <!-- accordion-body -->\n    </div> <!-- accordion-collapse -->\n  </div> <!-- accordion-item -->\n</div>\n<div style=\"margin-top: 10px\">\n<a class=\"btn btn-primary disabled\" id=\"btn-download\" href=\"\">Download</a>\n<button class=\"btn btn-primary\" id=\"btn-post\" value=\"8sSYVI0XjFqacEMZ8wF4\" disabled>Share publicly</button>\n</div>\n<script type=\"text/javascript\">\nvar MINIMUM_RATING_TIME = 10\n$('#qcslider').on('input', function() {\n\n    if ( (Date.now() - timestamp) / 1000 > MINIMUM_RATING_TIME) {\n        $('#btn-download').removeClass('disabled');\n        $('#btn-download').removeAttr('aria-disabled');\n        $('#btn-post').removeAttr('disabled');\n    };\n\n    $('#qcslider-collapse .list-group-item').removeClass(function(index, classname) {\n        return (classname.match(/(^|\\s)bg-\\S+/g) || []).join(' ');\n    });\n    $('#qcslider-collapse .list-group-item').removeClass(function(index, classname) {\n        return (classname.match(/(^|\\s)text-\\S+/g) || []).join(' ');\n    });\n\n    if ( $(this).val() < 1.5 ) {\n        $('#qcslider-collapse .list-group-item-danger').addClass('bg-danger text-white');\n    } else if ( $(this).val() > 3.5 ) {\n        $('#qcslider-collapse .list-group-item-success').addClass('bg-success text-white');\n    } else if ( $(this).val() < 2.5 ) {\n        $('#qcslider-collapse .list-group-item-warning').addClass('bg-warning text-dark');\n    } else {\n        $('#qcslider-collapse .list-group-item-primary').addClass('bg-primary text-white');\n    };\n\n    var payload = read_form();\n});\n\n$('#qcextra-confidence').on('input', function() {\n    if ( (Date.now() - timestamp) / 1000 > MINIMUM_RATING_TIME) {\n        $('#btn-download').removeClass('disabled');\n        $('#btn-download').removeAttr('aria-disabled');\n        $('#btn-post').removeAttr('disabled');\n    };\n\n    $('#qcextra-collapse .list-group-item').removeClass(function(index, classname) {\n        return (classname.match(/(^|\\s)bg-\\S+/g) || []).join(' ');\n    });\n    $('#qcextra-collapse .list-group-item').removeClass(function(index, classname) {\n        return (classname.match(/(^|\\s)text-\\S+/g) || []).join(' ');\n    });\n\n    if ( $(this).val() < 2.0 ) {\n        $('#qcextra-collapse .list-group-item-warning').addClass('bg-warning text-dark');\n    } else {\n        $('#qcextra-collapse .list-group-item-success').addClass('bg-success text-white');\n    };\n\n    var payload = read_form();\n});\n\n\n$('#qcextra-comments').bind('input propertychange', function() {\n    if ( (Date.now() - timestamp) / 1000 > MINIMUM_RATING_TIME) {\n        $('#btn-download').removeClass('disabled');\n        $('#btn-download').removeAttr('aria-disabled');\n        $('#btn-post').removeAttr('disabled');\n    };\n});\n\n$( '#btn-post' ).click( function() {\n    var payload = read_form();\n    var md5sum = \"unspecified\";\n    var params = {\n        'rating': payload['rating'],\n        'md5sum': md5sum,\n        'name': \"\",\n        'comment': JSON.stringify(payload['artifacts'])\n    };\n\n    // disable development releases\n    var authorization = $(this).val();\n    var ratingReq = new XMLHttpRequest();\n    ratingReq.open(\"POST\", \"https://mriqc.nimh.nih.gov:443/api/v1/rating\");\n    ratingReq.setRequestHeader(\"Content-Type\", \"application/json;charset=UTF-8\");\n    ratingReq.setRequestHeader(\"Authorization\", authorization);\n    ratingReq.onload = function () {\n        status = ratingReq.status;\n        $('#btn-post').removeClass('btn-primary');\n        $('#btn-post').attr('disabled', true);\n        $('#btn-post').attr('aria-disabled', true);\n        $('#btn-post').prop('disabled');\n        $('#btn-post').addClass('disabled');\n        $('#btn-post').removeClass('active');\n        if (status === \"201\") {\n            $('#btn-post').addClass('btn-success');\n            $('#btn-post').html('Posted!');\n        } else {\n            $('#btn-post').addClass('btn-danger');\n            $('#btn-post').html('Failed');\n        };\n    };\n    ratingReq.send(JSON.stringify(params));\n});\n\n$( 'body' ).on( 'click', '#qcartifacts-group input', function(e) {\n    if ( (Date.now() - timestamp) / 1000 > MINIMUM_RATING_TIME) {\n        $('#btn-download').removeClass('disabled');\n        $('#btn-download').removeAttr('aria-disabled');\n        $('#btn-post').removeAttr('disabled');\n    };\n    \n    var payload = read_form();\n});\n\n$( 'body' ).on( 'click', '#qcrating-toggler', function(e) {\n    toggle_rating();\n});\n</script>\n</div>\n\n<script type=\"text/javascript\">\nfunction toggle(id) {\n    var element = document.getElementById(id);\n    if(element.style.display == 'block')\n        element.style.display = 'none';\n    else\n        element.style.display = 'block';\n}\n</script>\n</body>\n</html>"
  },
  {
    "path": "docs/source/about.rst",
    "content": "Introduction\n************\n\n.. include:: ../../README.rst\n   :start-line: 3\n"
  },
  {
    "path": "docs/source/changes.rst",
    "content": "What's new\n**********\n\n.. include:: ../../CHANGES.rst\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "\"\"\"\nmriqc documentation build configuration file, created by\nsphinx-quickstart on Thu Feb 25 09:31:58 2016.\n\nThis file is execfile()d with the current directory set to its\ncontaining dir.\n\nNote that not all possible configuration values are present in this\nautogenerated file.\n\nAll configuration values have a default; values that are commented out\nserve to show the default.\n\"\"\"\n\nimport os\n\nfrom packaging.version import Version\n\nfrom mriqc import __copyright__, __version__\n\n# Disable etelemetry during doc builds\nos.environ['NIPYPE_NO_ET'] = '1'\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n\n# -- General configuration ------------------------------------------------\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx.ext.doctest',\n    'sphinx.ext.ifconfig',\n    'sphinx.ext.intersphinx',\n    'sphinx.ext.mathjax',\n    'sphinx.ext.napoleon',\n    'sphinx.ext.todo',\n    'sphinx.ext.viewcode',\n    'nipype.sphinxext.plot_workflow',\n    'sphinxarg.ext',  # argparse extension\n    # 'sphinx.ext.autosectionlabel',\n]\n\n# Mock modules in autodoc:\nautodoc_mock_imports = [\n    'dipy',\n    'matplotlib',\n    'nilearn',\n    'numpy',\n    'pandas',\n    'scipy',\n    'seaborn',\n    'sklearn',\n    'statsmodels',\n    'xgboost',\n]\n\nsuppress_warnings = ['image.nonlocal_uri']\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The encoding of source files.\n# source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = 'mriqc'\nauthor = 'The NiPreps Developers'\ncopyright = __copyright__\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The full version, including alpha/beta/rc tags.\nrelease = __version__\n# The short X.Y version.\nversion = Version(__version__).public\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = 'en'\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n# today = ''\n# Else, today_fmt is used as the format for a strftime call.\n# today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\nexclude_patterns = []\n\n# The reST default role (used for this markup: `text`) to use for all\n# documents.\n# default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n# add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n# add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n# show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n# modindex_common_prefix = []\n\n# If true, keep warnings as \"system message\" paragraphs in the built documents.\n# keep_warnings = False\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = True\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\nhtml_theme = 'sphinx_rtd_theme'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n# html_theme_options = {}\n\n# Add any paths that contain custom themes here, relative to this directory.\n# html_theme_path = []\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\n# html_title = None\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n# html_short_title = None\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n# html_logo = None\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n# html_favicon = None\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# Add any extra paths that contain custom files (such as robots.txt or\n# .htaccess) here, relative to this directory. These files are copied\n# directly to the root of the documentation.\n# html_extra_path = []\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\n# html_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n# html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n# html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n# html_additional_pages = {}\n\n# If false, no module index is generated.\n# html_domain_indices = True\n\n# If false, no index is generated.\nhtml_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n# html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n# html_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n# html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n# html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n# html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n# html_file_suffix = None\n\n# Language to be used for generating the HTML full-text search index.\n# Sphinx supports the following languages:\n#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'\n#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'\n# html_search_language = 'en'\n\n# A dictionary with options for the search language support, empty by default.\n# Now only 'ja' uses this config value\n# html_search_options = {'type': 'default'}\n\n# The name of a javascript file (relative to the configuration directory) that\n# implements a search results scorer. If empty, the default will be used.\n# html_search_scorer = 'scorer.js'\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'mriqcdoc'\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    # 'papersize': 'letterpaper',\n    # The font size ('10pt', '11pt' or '12pt').\n    # 'pointsize': '10pt',\n    # Additional stuff for the LaTeX preamble.\n    # 'preamble': '',\n    # Latex figure (float) alignment\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (\n        master_doc,\n        'mriqc.tex',\n        'mriqc Documentation',\n        author,\n        'manual',\n    ),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n# latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n# latex_use_parts = False\n\n# If true, show page references after internal links.\n# latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n# latex_show_urls = False\n\n# Documents to append as an appendix to all manuals.\n# latex_appendices = []\n\n# If false, no module index is generated.\n# latex_domain_indices = True\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [(master_doc, 'mriqc', 'mriqc Documentation', [author], 1)]\n\n# If true, show URL addresses after external links.\n# man_show_urls = False\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (\n        master_doc,\n        'mriqc',\n        'mriqc Documentation',\n        author,\n        'mriqc',\n        'One line description of project.',\n        'Miscellaneous',\n    ),\n]\n\n# Documents to append as an appendix to all manuals.\n# texinfo_appendices = []\n\n# If false, no module index is generated.\n# texinfo_domain_indices = True\n\n# How to display URL addresses: 'footnote', 'no', or 'inline'.\n# texinfo_show_urls = 'footnote'\n\n# If true, do not generate a @detailmenu in the \"Top\" node's menu.\n# texinfo_no_detailmenu = False\n\n\n# Example configuration for intersphinx: refer to the Python standard library.\nintersphinx_mapping = {\n    'bids': ('https://bids-standard.github.io/pybids/', None),\n    'matplotlib': ('https://matplotlib.org/', None),\n    'nibabel': ('https://nipy.org/nibabel/', None),\n    'nipype': ('https://nipype.readthedocs.io/en/latest/', None),\n    'numpy': ('https://numpy.org/doc/stable/', None),\n    'pandas': ('https://pandas.pydata.org/pandas-docs/dev/', None),\n    'python': ('https://docs.python.org/3/', None),\n    'scipy': ('https://docs.scipy.org/doc/scipy/reference', None),\n}\n"
  },
  {
    "path": "docs/source/dsa.rst",
    "content": "\n.. _dsa:\n\nData Sharing Agreement\n**********************\nFoundations\n-----------\n(Text in this section has been retrieved from https://www.ihris.org/toolkit/tools/data-sharing.html)\n\nData-sharing is an important way to increase the ability of researchers, scientists and policy-makers\nto analyze and translate data into meaningful reports and knowledge.\nSharing data discourages duplication of effort in data collection and encourages diverse thinking,\nas others are able to use the data to answer questions that the initial data collectors may not\nhave considered.\nSharing data also encourages accountability and transparency, enabling researchers to validate\none another's findings.\nFinally, data from multiple sources can often be combined to allow for comparisons that cross national\nand departmental lines.\n\nMRIQC DSA (Data Sharing Agreement)\n----------------------------------\nMRIQC extracts a vector of :abbr:`IQMs (image quality metrics)` from the input dataset.\nThese :abbr:`IQMs (image quality metrics)` are estimations of numerical properties of the\ndata array in the image, hence they are not usable in identifying data.\nIn other words, it is not possible to identify a natural person from this information.\nAdditionally, MRIQC collects metadata from the appropriate fields found in the BIDS\nstructure of the input dataset.\nAny information that could be used to identify the natural person the original data\nwere obtained from are stripped out (e.g. the subject identifier in the context of the\nstudy, date of acquisition, phenotypical information, etc.).\n\n**MRIQC does not share the original input dataset**.\nOnly an MD5 summary (*checksum*) of the input data is calculated and can be used to\ntrace back the original dataset which MRIQC extracted the information from.\nThis checksum cannot be used to regenerate the original data it was calculated from.\nTherefore, in the case that MRIQC was run on private data, the original images\nremain inaccessible to the public as no original data are shared.\n\nWithdrawing records and period of agreement\n...........................................\nIf you do not agree to these terms, please make use of the ``--no-sub`` (*do not submit*)\ncommand line flag with MRIQC.\nMRIQC will not collect any data when you OPT-OUT with this command line flag.\nIf you wish to withdraw :abbr:`IQMs (image quality metrics)` records from the database,\nplease send a request to the NiPreps Developers <nipreps@gmail.com> indicating the\nMD5 checksums of the datasets that are to be removed.\nIf any MD5 checksum matches any image publicly available or one or more associated\nquality rating records were found, then the corresponding record cannot be destroyed,\nwithdrawn or recalled.\nWithdrawing can be exercised within twelve months of the submission of the IQMs to\nthe Web-API.\nQuality ratings submitted via the \"Rating Widget\" of MRIQC reports or any other\nmeans cannot be withdrawn since it requires an active action from the submitter\nthat makes them aware that the data will be automatically shared publicly.\n\nThe MRIQC Web-API will keep collecting records until funding runs out.\nIn the case the service is shut down, existing records will be posted in a permanent,\nstatic storage service.\nThis agreement does not expire.\n\nIntended use of the data\n........................\nThe resource is particularly designed for researchers to share image quality metrics and\nannotations that can readily be reused in training human experts and machine learning\nalgorithms.\nThe ultimate goal of the MRIQC Web-API is to allow the development of fully automated\nquality control tools that outperform expert ratings in identifying degenerate images.\n\nConstraints on use of the data\n..............................\nData are shared under a CC0 (public domain) license.\n\nData confidentiality\n....................\nPlease use please make use of the ``--no-sub`` (*do not submit*) command line flag with MRIQC\nto ensure that your data remain confidential.\n\nFinancial costs of data-sharing\n...............................\nThe MRIQC Web-API infrastructure is funded by NIMH grants R24MH117179, and ZICMH002960.\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": "Welcome to *MRIQC*'s documentation!\n###################################\n\n.. include:: ../../README.rst\n   :start-line: 3\n\n.. image :: _static/OHBM2017-poster.png\n\nContents\n--------\n\n.. toctree::\n   :maxdepth: 3\n\n   about\n   install\n   usage\n   measures\n   reports\n   workflows\n   dsa\n   license\n   changes\n\nIndices and tables\n------------------\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/source/install.rst",
    "content": "\nInstalling *MRIQC*\n******************\n*MRIQC* is a *NiPreps* (`www.nipreps.org <https://nipreps.org>`__)\n*BIDS App* [BIDSApps]_.\nAs such, *MRIQC* can be installed manually (*Bare-metal installation*,\nsee below) or containerized.\nFor containerized execution with *Docker* or *Singularity*, please\nfollow the documentation on the *NiPreps* site\n(`introduction <https://www.nipreps.org/apps/framework/>`__).\n\n\"Bare-metal\" installation\n-------------------------\nIf, for some reason, you really need a custom installation,\n*MRIQC* can be installed as follows.\nFirst, please make sure you have the execution system dependencies\ninstalled (see below).\nSecond, the latest development version of MRIQC can be installed from\ngithub using ``pip`` on a Python 3 environment::\n\n  python -m pip install -U mriqc\n\n\n.. warning::\n\n        As of MRIQC 0.9.4, Python 2 is no longer supported.\n\n.. warning::\n\n        MRIQC uses matplotlib to create graphics. By default, matplotlib is configured to\n        plot through an interactive Tcl/tk interface, which requires a functional display to be available.\n        In *head-less* settings (for example, when running under tmux),\n        you may see an error::\n\n                _tkinter.TclError: couldn't connect to display \"localhost:10.0\"\n\n        There are two pathways to fix this issue.\n        One is setting up a virtual display with a tool like XVfb.\n        Alternatively, you can configure your matplotlib distribution to perform on\n        head-less mode by default.\n        That is achieved by uncommenting the ``backend : Agg`` line in the matplotlib's\n        configuration file.\n        The location of the configuration file can be retrieved with Python::\n\n          >>> import matplotlib\n          >>> print(matplotlib.matplotlib_fname())\n\n        Alternatively, you can issue the following shell command-line to edit this setting::\n\n        $ sed -i 's/\\(backend *: \\).*$/\\1Agg/g' $( python -c \"import matplotlib; print(matplotlib.matplotlib_fname())\" )\n\nExecution system dependencies\n.............................\nIf you are using a a `Neurodebian <http://neuro.debian.net/>`_ Linux distribution,\ninstallation should be as easy as::\n\n  sudo apt-get install afni ants\n  sudo ln -sf /usr/lib/ants/N4BiasFieldCorrection /usr/local/bin/\n\nAfter installation, make sure that all the necessary binaries are added to the ``$PATH`` environment\nvariable, for the user that will run ``mriqc``.\n\nOtherwise, you can follow each software installation guide:\n`AFNI <https://afni.nimh.nih.gov/afni/doc/howto/0>`_,\nand `ANTs <http://stnava.github.io/ANTs/>`_.\n\n.. warning::\n\n    Please note that *MRIQC* 22.0.0 and later requires Freesurfer's *SynthStrip* tool.\n\nPlease also install *FreeSurfer* (above 7.2) using `their guidelines <https://surfer.nmr.mgh.harvard.edu/fswiki/DownloadAndInstall>`__.\nIf your *FreeSurfer* version is older than 7.2, then you can get *MRIQC*'s requirements with::\n\n  wget https://github.com/freesurfer/freesurfer/blob/dev/mri_synthstrip/synthstrip.1.pt -P ${FREESURFER_HOME}/models/\n\n.. danger::\n\n        You will get the following error if you do not install *SynthStrip*'s requirement::\n\n          The 'model' trait of a _SynthStripInputSpec instance must be a pathlike object or string representing an existing file, but a value of '<undefined>' <class 'str'> was specified.`\n\n        If the *SynthStrip* requirement was downloaded, please make sure your environment has defined the variable ``$FREESURFER_HOME`` and that it is pointing at the right directory::\n\n          echo $FREESURFER_HOME\n\n        If the ``$FREESURFER_HOME`` environment variable is defined, check whether the model file is available at the expected path::\n\n          $ stat $FREESURFER_HOME/models/synthstrip.1.pt \n            File: /opt/freesurfer/models/synthstrip.1.pt\n            Size: 30851709    Blocks: 60264      IO Block: 4096   regular file\n          Device: fd00h/64768d  Inode: 12583379    Links: 1\n          Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)\n          Access: 2024-04-09 11:13:29.091893354 +0200\n          Modify: 2023-01-20 09:39:54.284056264 +0100\n          Change: 2023-01-20 09:39:54.284056264 +0100\n           Birth: 2023-01-20 09:39:54.224056213 +0100\n\n        If *SynthStrip*'s model file is not present, the output will look like::\n\n          $ stat $FREESURFER_HOME/models/synthstrip.1.pt\n          stat: cannot statx '/opt/freesurfer/models/synthstrip.1.pt': No such file or directory\n"
  },
  {
    "path": "docs/source/iqms/bold.rst",
    "content": ".. _iqms_bold:\n\nIQMs for functional images\n==========================\n.. automodule:: mriqc.qc.functional\n    :members:\n    :undoc-members:\n    :show-inheritance:\n"
  },
  {
    "path": "docs/source/iqms/dwi.rst",
    "content": ".. _iqms_dwi:\n\nIQMs for diffusion images\n=========================\n.. automodule:: mriqc.qc.diffusion\n    :members:\n    :undoc-members:\n    :show-inheritance:\n"
  },
  {
    "path": "docs/source/iqms/t1w.rst",
    "content": ".. _iqms_t1w:\n\nIQMs for structural images\n==========================\n.. automodule:: mriqc.qc.anatomical\n    :members:\n    :undoc-members:\n    :show-inheritance:\n"
  },
  {
    "path": "docs/source/license.rst",
    "content": "About the *NiPreps* framework licensing\n***************************************\nPlease check https://www.nipreps.org/community/licensing/ for detailed\ninformation on the criteria we use to license *MRIQC* and other\nprojects of the framework.\n\nLicense information\n-------------------\nCopyright (c) 2021, the *NiPreps* Developers.\n\nAs of the 21.0.x pre-release and release series, *MRIQC* is\nlicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\nCopyright (c) 2015-2021, the *MRIQC* developers and the CRN.\nAll rights reserved.\n\n*MRIQC* 0.16.x series and earlier are\nlicensed under the BSD 3-clause license.\nYou may obtain a copy of the License at\nhttps://opensource.org/licenses/BSD-3-Clause\n\nAll trademarks referenced herein are property of their respective holders.\n\nNotice\n------\n  .. include:: ../../NOTICE\n"
  },
  {
    "path": "docs/source/measures.rst",
    "content": "\n.. _measures:\n\nImage Quality Metrics (IQMs)\n****************************\n\n.. automodule:: mriqc.qc\n    :members:\n    :undoc-members:\n    :show-inheritance:\n"
  },
  {
    "path": "docs/source/reports/bold.rst",
    "content": "\n.. _reports-bold:\n\nBOLD images\n===========\nOne individual report per input functional timeseries will be generated\nin the path ``<output_dir>/reports/sub-IDxxx_task-name_bold.html```.\nAn example report is given\n`here <../_static/example_funcreport.html>`_.\n\nThe individual report for the functional images is structured as follows:\n\n.. _reports-bold-summary:\n\nSummary\n-------\nThe first section summarizes some important information:\n\n  * subject identifier, date and time of execution of\n    ``mriqc``, software version;\n  * workflow details and flags raised during execution; and\n  * the extracted IQMs.\n\n.. _reports-bold-visual:\n\nVisual reports\n--------------\nThe section with visual reports contains:\n\n#. Mosaic view of the average BOLD signal.\n\n   .. figure:: ../resources/reports-bold_mean.png\n     :alt: mean epi mosaic\n\n#. Mosaic view of the temporal standard deviation.\n\n   .. figure:: ../resources/reports-bold_sd.png\n     :alt: sd of epi mosaic\n\n#. Summary plot, showing the slice-wise\n   signal intensity at the extremes for the identification\n   of spikes, the outliers metric, the DVARS and the\n   :abbr:`FD (framewise displacement)`. Finally the\n   so-called carpetplot [Power2016]_. \n   The carpet plot rows correspond to voxelwise time series,\n   and are separated into regions: cortical gray matter, deep \n   gray matter, white matter and cerebrospinal fluid, cerebellum \n   and the brain-edge or “crown” [Provins2022]_.\n   The crown corresponds to the voxels located on a \n   closed band around the brain [Patriat2015]_.\n\n   .. figure:: ../resources/reports-bold_summary.png\n     :alt: fMRI summary plot\n\n.. _reports-bold-verbose:\n\nVerbose reports\n---------------\nIf mriqc was run with the ``--verbose-reports`` flag, the\nfollowing plots will be appended:\n\n#. Mosaic view of the average BOLD signal, zoomed-in\n   to the bounding box of brain activation.\n\n   .. figure:: ../resources/reports-bold_mean_zoom.png\n     :alt: zoomed mean epi mosaic\n\n#. Mosaic view of the average BOLD signal, with background\n   enhancement.\n\n   .. figure:: ../resources/reports-bold_mean_bg.png\n     :alt: mean epi background mosaic\n\n#. One rows of axial views at different Z-axis points\n   showing the calculated brain mask.\n\n   .. figure:: ../resources/reports-bold_mask.png\n     :alt: bold brainmasks\n\n#. Mosaic view with animation for assessment of the\n   co-registration to MNI space (roll over the image\n   to activate the animation).\n\n   .. figure:: ../resources/reports-bold_mni.png\n     :alt: bold-mni coregistration\n\n.. _reports-bold-metadata:\n\nMetadata\n--------\nIf some metadata was found in the BIDS structure, it is\nreported here.\n\n.. topic:: References\n\n  .. [Patriat2015] Patriat, R., EK Molloy, RM Birn, T. Guitchev, and A. Popov. “Using Edge Voxel Information to \n     Improve Motion Regression for Rs-FMRI Connectivity Studies.” Brain Connectivity 5, no. 9 (28 2015): 582–95. \n     doi: `10.1089/brain.2014.0321 <https://doi.org/10.1089/brain.2014.0321>`__.\n\n  .. [Power2016] Power JD, A simple but useful way to assess fMRI scan qualities.\n     NeuroImage. 2016. doi: `10.1016/j.neuroimage.2016.08.009 <http://doi.org/10.1016/j.neuroimage.2016.08.009>`__.\n\n  .. [Provins2022] Provins, Céline, Christopher J. Markiewicz, Rastko Ciric, Mathias Goncalves, César Caballero-Gaudes, \n     Russell Poldrack, Patric Hagmann, and Oscar Esteban. “Quality Control and Nuisance Regression of FMRI, Looking out \n     Where Signal Should Not Be Found.” OSF Preprints, January 21, 2022.\n     doi: `10.31219/osf.io/hz52v <https://doi.org/10.31219/osf.io/hz52v>`__.\n\n\n"
  },
  {
    "path": "docs/source/reports/group.rst",
    "content": ".. _reports-group:\n\nGroup reports\n=============\nOnce a sample has been processed with the appropriate\n:ref:`workflow <workflows>`, all the :abbr:`IQMs (image quality metrics)`\nwritten out in JSON files are collected in a\n:abbr:`CSV (comma separated values)` table, at the standard path\nof ``<output-dir>/<modality>.csv``. Therefore, for structural\nimages, the IQMs will be found in ``<output-dir>/T1w.csv``.\nCorrespondingly, ``<output-dir>/bold.csv`` is the designated path\nfor the functional IQM table.\n\nThe group report will process the ``<output-dir>/<modality>.csv``\ntable to generate one strip-plot per IQM:\n\n.. figure:: ../resources/reports-group_overview.png\n  :alt: appearance of the group reports\n\nThe strip-plots are interactive, so each sample (the value of a specific\nIQM associated to an input image) can be clicked to open up the corresponding\nindividual report.\nThis is particularly useful when trying to identify subpar images that\nshow themselves as outliers in the general distribution of the IQM:\n\n.. figure:: ../resources/reports-group_outlier.png\n  :alt: clicking on an outlier\n\nIn this example, the corresponding individual report for the selected\nsample will open up (`click here to see this example\nreport <http://web.stanford.edu/group/poldracklab/mriqc/reports/sub-51296_T1w.html>`_).\n\nThe boxplots shown are Tukey boxplots, with median and interquartile range shown.\nWhiskers extend to the farthest point that is within 1.5 * IQR of the upper (or lower) quartile.\n"
  },
  {
    "path": "docs/source/reports/smri.rst",
    "content": ".. _reports-smri:\n\nT1 and T2 -weighed images\n=========================\nOne individual report per input structural volume will be generated\nin the path ``<output_dir>/reports/sub-IDxxx_T1w.html```.\nAn example report is given\n`here <../_static/example_anatreport.html>`_.\n\nThe individual report for the structural images is\nstructured as follows:\n\n.. _reports-smri-summary:\n\nSummary\n-------\nThe first section summarizes some important information:\n\n  * subject identifier, date and time of execution of\n    ``mriqc``, software version;\n  * workflow details and flags raised during execution; and\n  * the extracted IQMs.\n\n.. _reports-smri-visual:\n\nVisual reports\n--------------\nThe section with visual reports contains:\n\n#. Mosaic view, zoomed-in over the parenchyma of the brain.\n\n   .. figure:: ../resources/reports-t1w_mosaic_zoom.png\n     :alt: zoomed in mosaic\n\n#. Mosaic view with background noise enhancement.\n\n   .. figure:: ../resources/reports-t1w_background.png\n     :alt: t1 background\n\n.. _reports-smri-verbose:\n\nVerbose reports\n---------------\nIf mriqc was run with the ``--verbose-reports`` flag, the\nfollowing plots will be appended:\n\n#. Mosaic view with animation for assessment of the\n   co-registration to MNI space (roll over the image\n   to activate the animation).\n\n   .. figure:: ../resources/reports-t1w_mni.svg\n     :alt: t1-mni coregistration\n\n#. Three rows of axial views at different Z-axis points\n   showing: the calculated brain mask, the segmentation\n   done with FSL FAST and a saturated view of the background.\n\n   .. figure:: ../resources/reports-t1w_masks1.png\n     :alt: t1 verbose masks 1\n\n#. Two rows of coronal views, showing the object/background\n   segmentation, and the air/no-air segmentation.\n\n   .. figure:: ../resources/reports-t1w_masks2.png\n     :alt: t1 verbose masks 2\n\n#. The :math:`\\chi^2` function fitting for the calculation\n   of the QI2:\n\n   .. figure:: ../resources/reports-t1w_qi2.png\n     :alt: t1 fitting of QI2\n\n.. _reports-smri-metadata:\n\nMetadata\n--------\nIf some metadata was found in the BIDS structure, it is\nreported here."
  },
  {
    "path": "docs/source/reports.rst",
    "content": "\n.. _reports:\n\nVisual Reports\n**************\nDemo: anatomical reports\n------------------------\n.. raw:: html\n\n    <iframe height=\"380px\" width=\"100%\" src=\"https://mfr.osf.io/render?url=https://osf.io/w3p6d/?action=download%26mode=render\" scrolling=\"yes\" marginheight=\"0\" frameborder=\"0\" allowfullscreen webkitallowfullscreen></iframe>\n\nDemo: functional reports\n------------------------\n\n.. raw:: html\n\n    <iframe height=\"370px\" width=\"100%\" src=\"https://mfr.osf.io/render?url=https://osf.io/hrnvw/?action=download%26mode=render\" scrolling=\"yes\" marginheight=\"0\" frameborder=\"0\" allowfullscreen webkitallowfullscreen></iframe>\n\nHow reports work\n----------------\n.. automodule:: mriqc.reports\n    :members:\n    :undoc-members:\n    :show-inheritance:\n"
  },
  {
    "path": "docs/source/resources/mriqc.sbatch",
    "content": "#!/bin/bash\n\n# NOTE: To use this script, edit lines 17, 26, 40, 55, 56, 58\n # labeled TODO with NOTES explaining how. \n\n# Set SBATCH Parameters: \n# ------------------------------------------\n# NOTE: These should work with Slurm HPC systems, \n # but these specific parameters  have only been tested on \n # Stanford's Sherlock. Some parameters may need to be \n # adjusted for other HPCs, specifically --partition.\n#SBATCH --job-name mriqc\n\n# NOTE: Each HCP has different partitions for job submissions. \n # Check your HCPs documentation for what partitions are available\n # to you. \n#SBATCH --partition normal #TODO: update for your HPC\n\n# NOTE: The --array parameter allows multiple jobs to be launched at once, \n # and is generally recommended to efficiently run several hundred jobs \n # simultaneously This should be adjusted to the range for your dataset;\n # 1-n%j where n is the number of \n # participants and j is the maximum number of concurrent jobs.  \n # In this example, there are 216 participants, and only 50 run\n # at a given time. \n#SBATCH --array=1-216%50  #TODO: adjust for your dataset\n\n# NOTE: These parameters request time and resources from the job\n # scheduler. These specific parameters should be sufficient for \n # most datasets. \n#SBATCH --time=1:00:00 #NOTE: likely longer than generally needed \n#SBATCH --ntasks 1\n#SBATCH --cpus-per-task=16\n#SBATCH --mem-per-cpu=4G\n\n# NOTE: These parameters set where log files will be written, and \n # where status emails will be sent. \n#SBATCH --output log/%x-%A-%a.out\n#SBATCH --error log/%x-%A-%a.err\n#SBATCH --mail-user=%u@stanford.edu #TODO: update to your email domain\n#SBATCH --mail-type=ALL \n\n## More information about these Slurm commands can be found at: \n # https://slurm.schedmd.com/sbatch.html\n# ------------------------------------------\n# Setup variables\n# ------------------------------------------\n# NOTE: These variables are paths to your data, and where you'd \n # like your output to be written. You should replace STUDY \n # with the directory one level below the directory where your data \n # is stored. \n # You should replace `ds0002785` with the name of your BIDS directory\nSTUDY=\"/scratch/users/mphagen/mriqc-protocol\" #TODO: replace with your path\nBIDS_DIR=\"${STUDY}/ds002785\"  # TODO: replace with path to your dataset\n\n# NOTE: These variables set the \"Apptainer\" execution for your \n # MRIQC container\nMRIQC_VERSION=\"24.0.2\" #TODO: update if using a different version\nAPPTAINER_CMD=\"apptainer run -e mriqc_${MRIQC_VERSION}.sif\"\n\nOUTPUT_DIR=\"${BIDS_DIR}/derivatives/mriqc-${MRIQC_VERSION}\"\n\n# NOTE: The next two variables are used to extract participant IDs from \n # the mandatory participants.tsv. SLURM_ARRAY_TASK_ID is generated by the \n #--array parameter, and is determined by each job's order - i.e. the \n # first job has the SLURM_ARRAY_TASK_ID=1, the second SLURM_ARRAY_TASK_ID=2.\n # It is necessary to add 1 to the SLURM_ARRAY_TASK_ID because of the header\n # in participants.tsv. \nsubject_idx=$(( ${SLURM_ARRAY_TASK_ID} + 1 ))\n \n## NOTE: The first clause in this line selects a row in participants.tsv \n # using subject_idx. This is piped to grep to isolate the subject id. \n # This regex should work for most subject naming conventions, but \n # may need to be modified in some cases.\nsubject=$( sed -n ${subject_idx}p ${BIDS_DIR}/participants.tsv \\\n | grep -oP \"sub-[A-Za-z0-9_]*\" ) \n\necho Subject $subject\n\n# Define the Apptainer command that will be ran, with MRIQC CLI flags\ncmd=\"${APPTAINER_CMD} ${BIDS_DIR} ${OUTPUT_DIR} participant \\\n     --participant-label $subject \\\n     -w $PWD/work/ \\\n     --omp-nthreads 10 --nprocs 12\"  # For nodes with at least 32GB RAM\n\n# Print useful information to log files\necho Running task ${SLURM_ARRAY_TASK_ID}\necho Commandline: $cmd\n\n# Run the full command defined in cmd \neval $cmd\nexitcode=$?\n\n# Print useful information to log files\necho \"sub-$subject   ${SLURM_ARRAY_TASK_ID}    $exitcode\" \\\n      >> ${SLURM_ARRAY_JOB_ID}.tsv\necho Finished tasks ${SLURM_ARRAY_TASK_ID} with exit code $exitcode\nexit $exitcode\n"
  },
  {
    "path": "docs/source/resources/sbatch.sh",
    "content": "#!/bin/bash\n#\n#SBATCH -J mriqc\n#SBATCH --array=1-13\n#SBATCH --time=136:00:00\n#SBATCH -n 1\n#SBATCH --cpus-per-task=16\n#SBATCH --mem-per-cpu=4G\n# Outputs ----------------------------------\n#SBATCH -o log/%x-%A-%a.out\n#SBATCH -e log/%x-%A-%a.err\n#SBATCH --mail-user=%u@gmail.com\n#SBATCH --mail-type=ALL\n# ------------------------------------------\n\nunset PYTHONPATH\n\nBIDS_DIR=\"$STUDY/ds000030\"\nOUTPUT_DIR=\"${BIDS_DIR}/derivatives/mriqc-0.16.1\"\n\nSINGULARITY_CMD=\"singularity run -e $STUDY/mriqc-0.16.1.simg\"\n\nsubject=$( sed -n -E \\\n    \"$((${SLURM_ARRAY_TASK_ID} + 1))s/sub (\\S*)\\>.*/\\1/gp\" \\\n    ${BIDS_DIR}/participants.tsv )\n\ncmd=\"${SINGULARITY_CMD} ${BIDS_DIR} ${OUTPUT_DIR} participant \\\n     --participant-label $subject \\\n     -w $L_SCRATCH/work/ \\\n     --omp-nthreads 8 --mem 10\"\necho Running task ${SLURM_ARRAY_TASK_ID}\necho Commandline: $cmd\neval $cmd\nexitcode=$?\necho \"sub-$subject   ${SLURM_ARRAY_TASK_ID}    $exitcode\" \\\n      >> ${SLURM_ARRAY_JOB_ID}.tsv\necho Finished tasks ${SLURM_ARRAY_TASK_ID} with exit code $exitcode\nexit $exitcode\n"
  },
  {
    "path": "docs/source/usage.rst",
    "content": "\n.. _running_mriqc:\n\nRunning *MRIQC*\n***************\n*MRIQC* is a `BIDS-App <http://bids-apps.neuroimaging.io/>`_ [BIDSApps]_,\nand therefore it inherently understands the :abbr:`BIDS (brain\nimaging data structure)` standard [BIDS]_.\nBefore moving forward, please make sure to have read and understood\n*NiPreps*'s\n`introductory documentation <https://www.nipreps.org/apps/framework/>`__).\n\nContainerized execution with *Docker* and *Singularity*/*Apptainer*\n-------------------------------------------------------------------\nFor containerized execution with *Docker* or *Singularity*/*Apptainer*, please\nfollow the documentation on the *NiPreps* site, which contains\ntip and troubleshooting guidelines for both\n`Docker <https://www.nipreps.org/apps/docker/>`__, and\n`Singularity or Apptainer <https://www.nipreps.org/apps/singularity/>`__.\nIn addition to container-specific guidelines, the documentation\nalso includes specific\n`help for processing DataLad-managed datasets <https://www.nipreps.org/apps/datalad/>`__.\n\nThe rest of this documentation page applies to both *bare-metal*\nand containerized execution modes.\n\n*MRIQC* can fetch data in *DataLad* datasets\n--------------------------------------------\nAs of version 22.0.3, *MRIQC* bundles *DataLad*, enabling automatic\ndata fetching in *DataLad* datasets.\nEmploying this feature in containerized environments may lead to\nsomewhat obscure errors (see, for example,\n`nipreps/mriqc#1307 <https://github.com/nipreps/mriqc/issues/1307>`__).\nIf you intend to use *DataLad* datasets, please read carefully\n*NiPreps*' `help for processing DataLad-managed datasets <https://www.nipreps.org/apps/datalad/>`__.\n\nAlternatively, this feature can be disabled by adding\n``--no-datalad-get`` to the command line.\nThis will separate *DataLad* management from *MRIQC*'s operation,\nwhich can be an effective way of debugging issues and averting\nerroneous conditions.\n\nTroubleshooting\n---------------\nIf you encounter problems, please check our\n`NiPreps Guidelines for Singularity or Apptainer <https://www.nipreps.org/apps/singularity/>`__.\nCommon tips and guidelines that used to be found within *MRIQC*'s\nor *fMRIPrep*'s documentation sites have been relocated in the\ngeneral *NiPreps* website.\n\nA *BIDS Apps* command line interface\n------------------------------------\n*MRIQC* follows the *BIDS Apps* standard command line interface::\n\n  mriqc bids-root/ output-folder/ participant\n\nThat simple command runs *MRIQC* on all the *T1w* and *BOLD* images found\nunder the BIDS-compliant folder ``bids-root/``.\nThe last ``participant`` keyword indicates that the first level analysis\nis run. (i.e. extracting the :abbr:`IQMs (image quality metrics)` from the\nimages retrieved within ``bids-root/``).\nThe second level (``group``) is automatically run if no particular subject\nis provided for analysis.\n\n.. note::\n\n   If the argument :code:`--participant-label` is not provided, then all\n   subjects will be processed and the group level analysis will\n   automatically be executed without need of running the command in item 3.\n\nTo specify one particular subject, the ``--participant-label`` argument\ncan be used::\n\n  mriqc bids-root/ output-folder/ participant --participant-label S01 S02 S03\n\nThat command will run MRIQC only on the subjects indicated: only\n``bids-root/sub-S01``, ``bids-root/sub-S02``, and ``bids-root/sub-S03``\nwill be processed.\nIn this case, the ``group`` level will not be triggered automatically.\nWe generate the ``group`` level results (the group level report and the\nfeatures CSV table) with: ::\n\n  mriqc bids-root/ output-folder/ group\n\nExamples of the generated visual reports are found\nin :ref:`The MRIQC Reports <reports>`.\n\n.. warning::\n\n    MRIQC by default attempts to upload anonymized quality metrics to a publicly accessible\n    web server (`mriqc.nimh.nih.gov <http://mriqc.nimh.nih.gov/>`_). The uploaded data consists\n    only of calculated quality metrics and scanning parameters. It removes all personal\n    health information and participant identifiers. We try to collect this data to build normal\n    distributions for improved outlier detection, but if you do not wish to participate you can\n    disable the submission with the ``--no-sub`` flag.\n\n.. topic:: BIDS data organization\n\n    The software automatically finds the data the input folder if it\n    follows the :abbr:`BIDS (brain imaging data structure)` standard [BIDS]_.\n    A fast and easy way to check that your dataset fulfills the\n    :abbr:`BIDS (brain imaging data structure)` standard is\n    the `BIDS validator <https://github.com/bids-standard/bids-validator>`_.\n\n.. topic:: BIDS-App levels\n\n    In the ``participant`` level, all individual images to be processed are run\n    through the pipeline, and the :ref:`MRIQC measures <measures>` are extracted and\n    the individual reports (see :ref:`The MRIQC Reports <reports>`) generated.\n    In the ``group`` level, the :abbr:`IQMs (image quality metrics)` extracted in\n    first place are combined in a table and the group reports are generated.\n\nCommand line interface\n......................\n.. argparse::\n   :ref: mriqc.cli.parser._build_parser\n   :prog: mriqc\n   :nodefault:\n   :nodefaultconst:\n\nRunning *MRIQC* on HPC with *Singularity*/*Apptainer*\n-----------------------------------------------------\nWe have profiled cores and memory usages with the *resource profiler*\ntool of *Nipype*.\n\nAn *MRIQC* run of one subject (from the ABIDE) dataset, containing only one\nrun, one BOLD task (resting-state) yielded the following report:\n\n  .. raw:: html\n\n      <iframe src=\"_static/bold-1subject-1task.html\" height=\"345px\" width=\"100%\"></iframe>\n\n  Using the ``MultiProc`` plugin of nipype with ``nprocs=10``, the workflow\n  nodes run across the available processors for 41.68 minutes.\n  A memory peak of 8GB is reached by the end of the runtime, when the\n  plotting nodes are fired up.\n\nWe also profiled MRIQC on a dataset with 8 tasks (one run per task),\non ds030 of OpenfMRI:\n\n  .. raw:: html\n\n      <iframe src=\"_static/bold-1subject-8tasks.html\" height=\"345px\" width=\"100%\"></iframe>\n\n  Again, we used ``n_procs=10``. The software run for roughly about the same\n  time (47.11 min). Most of the run time, memory usage keeps around a\n  maximum of 10GB. Since we saw a memory consumption of 1-2GB during the\n  the 1-task example, a rule of thumb may be that each task takes around\n  1GB of memory.\n  \n.. topic:: References\n\n  .. [BIDS] `Brain Imaging Data Structure <http://bids.neuroimaging.io/>`_\n  .. [BIDSApps] `BIDS-Apps: portable neuroimaging pipelines that understand BIDS\n     datasets <http://bids-apps.neuroimaging.io/>`_\n"
  },
  {
    "path": "docs/source/workflows.rst",
    "content": "\n.. _workflows:\n\nWorkflows\n*********\n\n.. automodule:: mriqc.workflows\n    :members:\n    :undoc-members:\n    :show-inheritance:\n"
  },
  {
    "path": "long_description.rst",
    "content": "MRIQC provides a series of image processing workflows\nto extract and compute a series of NR (no-reference), IQMs\n(image quality metrics) to be used in QAPs (quality\nassessment protocols) for MRI (magnetic\nresonance imaging)."
  },
  {
    "path": "mriqc/__init__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nThe mriqc package provides a series of :abbr:`NR (no-reference)`,\n:abbr:`IQMs (image quality metrics)` to used in :abbr:`QAPs (quality\nassessment protocols)` for :abbr:`MRI (magnetic resonance imaging)`.\n\"\"\"\n\nfrom mriqc._version import __version__\n\n__copyright__ = 'Copyright 2016-2024, The NiPreps Developers'\n__download__ = f'https://github.com/nipreps/mriqc/archive/{__version__}.tar.gz'\n__all__ = ['__version__', '__copyright__', '__download__']\n"
  },
  {
    "path": "mriqc/__main__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2022 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nfrom .cli.run import main\n\nif __name__ == '__main__':\n    import sys\n\n    from . import __name__ as module\n\n    # `python -m <module>` typically displays the command as __main__.py\n    if '__main__.py' in sys.argv[0]:\n        sys.argv[0] = f'{sys.executable} -m {module}'\n    main()\n"
  },
  {
    "path": "mriqc/_warnings.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Manipulate Python warnings.\"\"\"\n\nimport logging\nimport sys\n\nfrom nipype import logging as nlogging\n\nlogging.addLevelName(26, 'BANNER')  # Add a new level for banners\nlogging.addLevelName(25, 'IMPORTANT')  # Add a new level between INFO and WARNING\nlogging.addLevelName(15, 'VERBOSE')  # Add a new level between INFO and DEBUG\n\nLOGGER_FMT = (\n    '%(asctime)s |{color} %(levelname)-8s {reset}|{color} %(name)-16s '\n    '{reset}|{color} %(message)s{reset}'\n)\nDATE_FMT = '%Y-%m-%d %H:%M:%S'\nCONSOLE_COLORS = {\n    logging.DEBUG: '\\x1b[38;20m',\n    logging.INFO: '\\x1b[34;20m',\n    25: '\\x1b[33;20m',\n    logging.WARNING: '\\x1b[93;20m',\n    logging.ERROR: '\\x1b[31;20m',\n    logging.CRITICAL: '\\x1b[31;1m',\n    'reset': '\\x1b[0m',\n}\n\n\nclass _LogFormatter(logging.Formatter):\n    \"\"\"Customize the log format.\"\"\"\n\n    _colored = True\n\n    def __init__(self, datefmt=None, colored=True, **kwargs):\n        self._colored = colored\n        super().__init__(\n            datefmt=datefmt or DATE_FMT,\n            fmt=LOGGER_FMT.format(\n                color=CONSOLE_COLORS['reset'] if colored else '',\n                reset=CONSOLE_COLORS['reset'] if colored else '',\n            ),\n        )\n\n    def format(self, record):\n        reset = CONSOLE_COLORS['reset'] if self._colored else ''\n        self._style._fmt = (\n            '%(message)s'\n            if record.levelno == 26\n            else LOGGER_FMT.format(\n                color=CONSOLE_COLORS.get(\n                    record.levelno,\n                    CONSOLE_COLORS['reset'],\n                )\n                if self._colored\n                else '',\n                reset=reset,\n            )\n        )\n        return super().format(record)\n\n\nnlogging.getLogger('nipype')\n_wlog = logging.getLogger('py.warnings')\n_numexprlog = logging.getLogger('numexpr.utils')\n_dataladlog = logging.getLogger('datalad')\n\nfor logger_name in logging.root.manager.loggerDict:\n    logging.getLogger(logger_name).handlers.clear()\n\n_root_logger = logging.getLogger()\n# _root_logger.handlers.clear()\n_handler = logging.StreamHandler(stream=sys.stdout)\n_handler.setFormatter(_LogFormatter())\n_root_logger.addHandler(_handler)\n\n_wlog.addHandler(logging.NullHandler())\n_numexprlog.addHandler(logging.NullHandler())\n_dataladlog.addHandler(logging.NullHandler())\n\n# def _warn(message, category=None, stacklevel=1, source=None):\n#     \"\"\"Redefine the warning function.\"\"\"\n#     if category is not None:\n#         category = type(category).__name__\n#         category = category.replace('type', 'WARNING')\n\n#     logging.getLogger('py.warnings').debug(f\"{category or 'WARNING'}: {message}\")\n\n\n# def _showwarning(message, category, filename, lineno, file=None, line=None):\n#     _warn(message, category=category)\n\n\n# warnings.warn = _warn\n# warnings.showwarning = _showwarning\n\n# warnings.filterwarnings(\"ignore\", category=FutureWarning)\n# warnings.filterwarnings(\"ignore\", category=DeprecationWarning)\n# warnings.filterwarnings(\"ignore\", category=ResourceWarning)\n# # cmp is not used by mriqc, so ignore nipype-generated warnings\n# warnings.filterwarnings(\"ignore\", \"cmp not installed\")\n# warnings.filterwarnings(\n#     \"ignore\", \"This has not been fully tested. Please report any failures.\"\n# )\n# warnings.filterwarnings(\"ignore\", \"sklearn.externals.joblib is deprecated in 0.21\")\n# warnings.filterwarnings(\"ignore\", \"can't resolve package from __spec__ or __package__\")\n"
  },
  {
    "path": "mriqc/bin/__init__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\n``mriqc``'s executables.\n\"\"\"\n"
  },
  {
    "path": "mriqc/bin/abide2bids.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"ABIDE2BIDS download tool.\"\"\"\n\nfrom __future__ import annotations\n\nimport errno\nimport json\nimport os\nimport os.path as op\nimport shutil\nimport subprocess as sp\nimport tempfile\nfrom argparse import ArgumentParser, RawTextHelpFormatter\nfrom multiprocessing import Pool\n\nimport numpy as np\nfrom defusedxml import ElementTree as et\n\nfrom mriqc.bin import messages\n\n_curl_cmd = shutil.which('curl')\n_unzip_cmd = shutil.which('unzip')\n\n\ndef main():\n    \"\"\"Entry point.\"\"\"\n    parser = ArgumentParser(\n        description='ABIDE2BIDS downloader.',\n        formatter_class=RawTextHelpFormatter,\n    )\n    g_input = parser.add_argument_group('Inputs')\n    g_input.add_argument('-i', '--input-abide-catalog', action='store', required=True)\n    g_input.add_argument('-n', '--dataset-name', action='store', default='ABIDE Dataset')\n    g_input.add_argument('-u', '--nitrc-user', action='store', default=os.getenv('NITRC_USER'))\n    g_input.add_argument(\n        '-p',\n        '--nitrc-password',\n        action='store',\n        default=os.getenv('NITRC_PASSWORD'),\n    )\n\n    g_outputs = parser.add_argument_group('Outputs')\n    g_outputs.add_argument('-o', '--output-dir', action='store', default='ABIDE-BIDS')\n\n    opts = parser.parse_args()\n\n    if opts.nitrc_user is None or opts.nitrc_password is None:\n        raise RuntimeError('NITRC user and password are required')\n\n    dataset_desc = {\n        'BIDSVersion': '1.0.0rc3',\n        'License': 'CC Attribution-NonCommercial-ShareAlike 3.0 Unported',\n        'Name': opts.dataset_name,\n    }\n\n    out_dir = op.abspath(opts.output_dir)\n    try:\n        os.makedirs(out_dir)\n    except OSError as exc:\n        if exc.errno != errno.EEXIST:\n            raise exc\n\n    with open(op.join(out_dir, 'dataset_description.json'), 'w') as dfile:\n        json.dump(dataset_desc, dfile)\n\n    catalog = et.parse(opts.input_abide_catalog).getroot()\n    urls = [el.get('URI') for el in catalog.iter() if el.get('URI') is not None]\n\n    pool = Pool()\n    args_list = [(url, opts.nitrc_user, opts.nitrc_password, out_dir) for url in urls]\n    res = pool.map(fetch, args_list)\n\n    tsv_data = np.array([('subject_id', 'site_name')] + res)\n    np.savetxt(\n        op.join(out_dir, 'participants.tsv'),\n        tsv_data,\n        fmt='%s',\n        delimiter='\\t',\n    )\n\n\ndef fetch(args: (str, str, str, str)) -> (str, str):\n    \"\"\"\n    Downloads a subject and formats it into BIDS.\n\n    Parameters\n    ----------\n    args : Tuple[str, str, str, str]\n        URL, NITRC user, NITRC password, destination\n\n    Returns\n    -------\n    Tuple[str, str]\n        Subject ID, Site name\n    \"\"\"\n    out_dir = None\n    if len(args) == 3:\n        url, user, password = args\n    else:\n        url, user, password, out_dir = args\n\n    tmpdir = tempfile.mkdtemp()\n    if out_dir is None:\n        out_dir = os.getcwd()\n    else:\n        out_dir = op.abspath(out_dir)\n\n    pkg_id = [u[9:] for u in url.split('/') if u.startswith('NITRC_IR_')][0]\n    sub_file = op.join(tmpdir, f'{pkg_id}.zip')\n\n    cmd = [_curl_cmd, '-s', '-u', f'{user}:{password}', '-o', sub_file, url]\n    sp.check_call(cmd)\n    sp.check_call([_unzip_cmd, '-qq', '-d', tmpdir, '-u', sub_file])\n\n    abide_root = op.join(tmpdir, 'ABIDE')\n    files = []\n    for root, path, fname in os.walk(abide_root):\n        if fname and (fname[0].endswith('nii') or fname[0].endswith('nii.gz')):\n            if path:\n                root = op.join(root, path[0])\n            files.append(op.join(root, fname[0]))\n\n    index = len(abide_root) + 1\n    site_name, sub_str = files[0][index:].split('/')[0].split('_')\n    subject_id = 'sub-' + sub_str\n\n    for i in files:\n        ext = '.nii.gz'\n        if i.endswith('.nii'):\n            ext = '.nii'\n        if 'mprage' in i:\n            bids_dir = op.join(out_dir, subject_id, 'anat')\n            try:\n                os.makedirs(bids_dir)\n            except OSError as exc:\n                if exc.errno != errno.EEXIST:\n                    raise exc\n            shutil.copy(i, op.join(bids_dir, subject_id + '_T1w' + ext))\n\n        if 'rest' in i:\n            bids_dir = op.join(out_dir, subject_id, 'func')\n            try:\n                os.makedirs(bids_dir)\n            except OSError as exc:\n                if exc.errno != errno.EEXIST:\n                    raise exc\n            shutil.copy(i, op.join(bids_dir, subject_id + '_rest_bold' + ext))\n\n    shutil.rmtree(tmpdir, ignore_errors=True, onerror=_myerror)\n\n    success_message = messages.ABIDE_SUBJECT_FETCHED.format(\n        subject_id=subject_id[4:], site_name=site_name\n    )\n    print(success_message)\n    return subject_id[4:], site_name\n\n\ndef _myerror(message: str):\n    \"\"\"\n    Print warning in case an exception is raised for temporal files removal.\n\n    Parameters\n    ----------\n    message : str\n        `shutil.rmtree()` error message\n    \"\"\"\n    warning = messages.ABIDE_TEMPORAL_WARNING.format(message=message)\n    print(warning)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "mriqc/bin/dfcheck.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nCompares pandas dataframes by columns.\n\"\"\"\n\nimport sys\nfrom argparse import ArgumentParser, RawTextHelpFormatter\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\n\nfrom mriqc.bin import messages\nfrom mriqc.utils.misc import BIDS_COMP\n\n\ndef read_iqms(feat_file):\n    \"\"\"Read in a features table.\"\"\"\n    feat_file = Path(feat_file)\n\n    if feat_file.suffix == '.csv':\n        x_df = pd.read_csv(feat_file, index_col=False, dtype=dict.fromkeys(BIDS_COMP, str))\n        # Find present bids bits and sort by them\n        bids_comps_present = list(set(x_df.columns) & set(BIDS_COMP))\n        bids_comps_present = [bit for bit in BIDS_COMP if bit in bids_comps_present]\n        x_df = x_df.sort_values(by=bids_comps_present)\n        # Remove sub- prefix in subject_id\n        x_df.subject_id = x_df.subject_id.str.lstrip('sub-')\n\n        # Remove columns that are not IQMs\n        feat_names = x_df._get_numeric_data().columns.tolist()\n        for col in BIDS_COMP:\n            try:\n                feat_names.remove(col)\n            except ValueError:\n                pass\n    else:\n        bids_comps_present = ['subject_id']\n        x_df = pd.read_csv(feat_file, index_col=False, sep='\\t', dtype={'bids_name': str})\n        x_df = x_df.sort_values(by=['bids_name'])\n        x_df['subject_id'] = x_df.bids_name.str.lstrip('sub-')\n        x_df = x_df.drop(columns=['bids_name'])\n        x_df.subject_id = ['_'.join(v.split('_')[:-1]) for v in x_df.subject_id.ravel()]\n        feat_names = x_df._get_numeric_data().columns.tolist()\n\n    for col in feat_names:\n        if col.startswith(('size_', 'spacing_', 'Unnamed')):\n            feat_names.remove(col)\n\n    return x_df, feat_names, bids_comps_present\n\n\ndef main():\n    \"\"\"Entry point.\"\"\"\n    parser = ArgumentParser(\n        description='Compare two pandas dataframes.',\n        formatter_class=RawTextHelpFormatter,\n    )\n    g_input = parser.add_argument_group('Inputs')\n    g_input.add_argument(\n        '-i',\n        '--input-csv',\n        action='store',\n        type=Path,\n        required=True,\n        help='input data frame',\n    )\n    g_input.add_argument(\n        '-r',\n        '--reference-csv',\n        action='store',\n        type=Path,\n        required=True,\n        help='reference dataframe',\n    )\n    g_input.add_argument(\n        '--tolerance',\n        type=float,\n        default=1.0e-5,\n        help='relative tolerance for comparison',\n    )\n\n    opts = parser.parse_args()\n\n    ref_df, ref_names, ref_bids = read_iqms(opts.reference_csv)\n    tst_df, tst_names, tst_bids = read_iqms(opts.input_csv)\n\n    ref_df.set_index(ref_bids)\n    tst_df.set_index(tst_bids)\n\n    if sorted(ref_bids) != sorted(tst_bids):\n        sys.exit(messages.DFCHECK_DIFFERENT_BITS)\n\n    if sorted(ref_names) != sorted(tst_names):\n        sys.exit(messages.DFCHECK_CSV_COLUMNS)\n\n    ref_df = ref_df.sort_values(by=ref_bids)\n    tst_df = tst_df.sort_values(by=tst_bids)\n\n    if len(ref_df) != len(tst_df):\n        different_length_message = messages.DFCHECK_DIFFERENT_LENGTH.format(\n            len_input=len(ref_df), len_reference=len(tst_df)\n        )\n        print(different_length_message)\n        tst_rows = tst_df[tst_bids]\n        ref_rows = ref_df[ref_bids]\n\n        print(tst_rows.shape, ref_rows.shape)\n\n        tst_keep = np.sum(tst_rows.isin(ref_rows).values.ravel().tolist())\n        print(tst_keep)\n\n    diff = ~np.isclose(ref_df[ref_names].values, tst_df[tst_names].values, rtol=opts.tolerance)\n    if np.any(diff):\n        # ne_stacked = pd.DataFrame(data=diff, columns=ref_names).stack()\n        # ne_stacked = np.isclose(ref_df[ref_names], tst_df[ref_names]).stack()\n        # changed = ne_stacked[ne_stacked]\n        # changed.set_index(ref_bids)\n        difference_locations = np.where(diff)\n        changed_from = ref_df[ref_names].values[difference_locations]\n        changed_to = tst_df[ref_names].values[difference_locations]\n        cols = [ref_names[v] for v in difference_locations[1]]\n        bids_df = ref_df.loc[difference_locations[0], ref_bids].reset_index()\n        chng_df = pd.DataFrame({'iqm': cols, 'from': changed_from, 'to': changed_to})\n        table = pd.concat([bids_df, chng_df], axis=1)\n        print(table[ref_bids + ['iqm', 'from', 'to']].to_string(index=False))\n\n        corr = pd.DataFrame()\n        corr['iqms'] = ref_names\n        corr['cc'] = [\n            float(\n                np.corrcoef(\n                    ref_df[[var]].values.ravel(),\n                    tst_df[[var]].values.ravel(),\n                    rowvar=False,\n                )[0, 1]\n            )\n            for var in ref_names\n        ]\n\n        if np.any(corr.cc < 0.95):\n            iqms = corr[corr.cc < 0.95]\n            iqms_message = messages.DFCHECK_IQMS_UNDER_095.format(iqms=iqms)\n            print(iqms_message)\n            sys.exit(messages.DFCHECK_CSV_CHANGED)\n        else:\n            print(messages.DFCHECK_IQMS_CORRELATED)\n\n    sys.exit(0)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "mriqc/bin/fs2gif.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nBatch export freesurfer results to animated gifs.\n\"\"\"\n\nimport os\nimport os.path as op\nimport subprocess as sp\nfrom argparse import ArgumentParser, RawTextHelpFormatter\nfrom errno import EEXIST\nfrom shutil import rmtree, which\nfrom tempfile import mkdtemp\n\nimport nibabel as nb\nimport numpy as np\nfrom skimage import exposure\n\n\ndef main():\n    \"\"\"Entry point\"\"\"\n    parser = ArgumentParser(\n        description='Batch export freesurfer results to animated gifs.',\n        formatter_class=RawTextHelpFormatter,\n    )\n    g_input = parser.add_argument_group('Inputs')\n    g_input.add_argument('-s', '--subject-id', action='store')\n    g_input.add_argument('-t', '--temp-dir', action='store')\n    g_input.add_argument('--keep-temp', action='store_true', default=False)\n    g_input.add_argument('--zoom', action='store_true', default=False)\n    g_input.add_argument('--hist-eq', action='store_true', default=False)\n    g_input.add_argument('--use-xvfb', action='store_true', default=False)\n\n    g_outputs = parser.add_argument_group('Outputs')\n    g_outputs.add_argument('-o', '--output-dir', action='store', default='fs2gif')\n\n    opts = parser.parse_args()\n\n    if opts.temp_dir is None:\n        tmpdir = mkdtemp()\n    else:\n        tmpdir = op.abspath(opts.temp_dir)\n        try:\n            os.makedirs(tmpdir)\n        except OSError as exc:\n            if exc.errno != EEXIST:\n                raise exc\n\n    out_dir = op.abspath(opts.output_dir)\n    try:\n        os.makedirs(out_dir)\n    except OSError as exc:\n        if exc.errno != EEXIST:\n            raise exc\n\n    subjects_dir = os.getenv('SUBJECTS_DIR', op.abspath('subjects'))\n    subject_list = [opts.subject_id]\n    if opts.subject_id is None:\n        subject_list = [\n            op.basename(name)\n            for name in os.listdir(subjects_dir)\n            if op.isdir(os.path.join(subjects_dir, name))\n        ]\n    environ = os.environ.copy()\n    environ['SUBJECTS_DIR'] = subjects_dir\n    if opts.use_xvfb:\n        environ['doublebufferflag'] = 1\n\n    # tcl_file = pkgr.resource_filename('mriqc', 'data/fsexport.tcl')\n    tcl_contents = \"\"\"\nSetOrientation 0\nSetCursor 0 128 128 128\nSetDisplayFlag 3 0\nSetDisplayFlag 22 1\nset i 0\n\"\"\"\n\n    for subid in subject_list:\n        sub_path = op.join(subjects_dir, subid)\n        tmp_sub = op.join(tmpdir, subid)\n        try:\n            os.makedirs(tmp_sub)\n        except OSError as exc:\n            if exc.errno != EEXIST:\n                raise exc\n\n        data = nb.load(op.join(sub_path, 'mri', 'norm.mgz')).get_fdata()\n        data[data > 0] = 1\n\n        # Compute brain bounding box\n        indexes = np.argwhere(data)\n        bbox_min = indexes.min(0)\n        bbox_max = indexes.max(0) + 1\n        center = np.average([bbox_min, bbox_max], axis=0)\n\n        if opts.hist_eq:\n            ref_file = op.join(tmp_sub, f'{subid}.mgz')\n            img = nb.load(op.join(sub_path, 'mri', 'norm.mgz'))\n            data = exposure.equalize_adapthist(img.get_fdata(), clip_limit=0.03)\n            nb.MGHImage(data, img.affine, img.header).to_filename(ref_file)\n\n        if not opts.zoom:\n            # Export tiffs for left hemisphere\n            tcl_file = op.join(tmp_sub, f'{subid}.tcl')\n            with open(tcl_file, 'w') as tclfp:\n                tclfp.write(tcl_contents)\n                tclfp.write(\n                    f'for {{ set slice {bbox_min[2]} }} {{ $slice < {bbox_max[2]} }} {{ incr slice }} {{'\n                )\n                tclfp.write('    SetSlice $slice\\n')\n                tclfp.write('    RedrawScreen\\n')\n                tclfp.write(f'    SaveTIFF [format \"{tmp_sub}/{subid}-%03d.tif\" $i]\\n')\n                tclfp.write('    incr i\\n')\n                tclfp.write('}\\n')\n                tclfp.write('QuitMedit\\n')\n            cmd = [\n                'tkmedit',\n                subid,\n                'T1.mgz',\n                'lh.pial',\n                '-aux-surface',\n                'rh.pial',\n                '-tcl',\n                tcl_file,\n            ]\n            if opts.use_xvfb:\n                cmd = _xvfb_run() + cmd\n\n            print('Running tkmedit: {}'.format(' '.join(cmd)))\n            sp.call(cmd, env=environ)\n            # Convert to animated gif\n            print('Stacking coronal slices')\n            sp.call(\n                [\n                    which('convert'),\n                    '-delay',\n                    '10',\n                    '-loop',\n                    '0',\n                    f'{tmp_sub}/{subid}-*.tif',\n                    f'{out_dir}/{subid}.gif',\n                ]\n            )\n\n        else:\n            # Export tiffs for left hemisphere\n            tcl_file = op.join(tmp_sub, f'lh-{subid}.tcl')\n            with open(tcl_file, 'w') as tclfp:\n                tclfp.write(tcl_contents)\n                tclfp.write('SetZoomLevel 2')\n                tclfp.write(\n                    'for {{ set slice {bbox_min[2]} }} {{ $slice < {bbox_max[2]} }} {{ incr slice }} {{'\n                )\n                tclfp.write(f'    SetZoomCenter {center[0] + 30} {center[1] - 10} $slice\\n')\n                tclfp.write('    SetSlice $slice\\n')\n                tclfp.write('    RedrawScreen\\n')\n                tclfp.write(f'    SaveTIFF [format \"{tmp_sub}/{subid}-lh-%03d.tif\" $i]\\n')\n                tclfp.write('    incr i\\n')\n                tclfp.write('}\\n')\n                tclfp.write('QuitMedit\\n')\n            cmd = ['tkmedit', subid, 'norm.mgz', 'lh.white', '-tcl', tcl_file]\n            if opts.use_xvfb:\n                cmd = _xvfb_run() + cmd\n\n            print('Running tkmedit: {}'.format(' '.join(cmd)))\n            sp.call(cmd, env=environ)\n            # Convert to animated gif\n            print('Stacking coronal slices')\n\n            # Export tiffs for right hemisphere\n            tcl_file = op.join(tmp_sub, f'rh-{subid}.tcl')\n            with open(tcl_file, 'w') as tclfp:\n                tclfp.write(tcl_contents)\n                tclfp.write('SetZoomLevel 2')\n                tclfp.write(\n                    'for {{ set slice {bbox_min[2]} }} {{ $slice < {bbox_max[2]} }} {{ incr slice }} {{'\n                )\n                tclfp.write(f'    SetZoomCenter {center[0] - 30} {center[1] - 10} $slice\\n')\n                tclfp.write('    SetSlice $slice\\n')\n                tclfp.write('    RedrawScreen\\n')\n                tclfp.write(f'    SaveTIFF [format \"{tmp_sub}/{subid}-rh-%03d.tif\" $slice]\\n')\n                tclfp.write('    incr i\\n')\n                tclfp.write('}\\n')\n                tclfp.write('QuitMedit\\n')\n            cmd = ['tkmedit', subid, 'norm.mgz', 'rh.white', '-tcl', tcl_file]\n            if opts.use_xvfb:\n                cmd = _xvfb_run() + cmd\n\n            print('Running tkmedit: {}'.format(' '.join(cmd)))\n            sp.call(cmd, env=environ)\n            # Convert to animated gif\n            print('Stacking coronal slices')\n            sp.call(\n                [\n                    which('convert'),\n                    '-delay',\n                    '10',\n                    '-loop',\n                    '0',\n                    f'{tmp_sub}/{subid}-lh-*.tif',\n                    f'{out_dir}/{subid}-lh.gif',\n                ]\n            )\n            sp.call(\n                [\n                    which('convert'),\n                    '-delay',\n                    '10',\n                    '-loop',\n                    '0',\n                    f'{tmp_sub}/{subid}-rh-*.tif',\n                    f'{out_dir}/{subid}-rh.gif',\n                ]\n            )\n\n        if not opts.keep_temp:\n            rmtree(tmp_sub, ignore_errors=True, onerror=_myerror)\n\n\ndef _xvfb_run(wait=5, server_args='-screen 0, 1600x1200x24', logs=None):\n    \"\"\"\n    Wrap command with xvfb-run. Copied from:\n    https://github.com/VUIIS/seam/blob/1dabd9ca5b1fc7d66ef7d41c34ea8d42d668a484/seam/util.py\n\n    \"\"\"\n    if logs is None:\n        logs = op.join(mkdtemp(), 'fs2gif_xvfb')\n\n    return [\n        'xvfb-run',\n        '-a',  # automatically get a free server number\n        f'-f {logs}.out',\n        f'-e {logs}.err',\n        f'--wait={wait:d}',\n        f'--server-args=\"{server_args}\"',\n    ]\n\n\ndef _myerror(msg):\n    print(f'WARNING: Error deleting temporal files: {msg}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "mriqc/bin/labeler.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nimport csv\nimport os\nimport random\nimport sys\nimport webbrowser\n\nimport numpy as np\n\n\ndef num_rows(data):\n    for j in range(1, 4):\n        if len(data[j]) == 0:\n            return j\n    return 4\n\n\ndef main():\n    \"\"\"read the input file\"\"\"\n    print('Reading file sinfo.csv')\n    csvfile = open('sinfo.csv', 'rb')\n    csvreader = csv.reader(csvfile)\n    file = list(csvreader)\n\n    # display statistics\n    finished = [0.0, 0.0, 0.0]\n    hold = np.zeros((3, len(file) - 1))\n    hold[:] = np.nan\n    total = 601\n    for i in range(1, len(file)):\n        for j in range(1, 4):\n            if len(file[i][j]) > 0:\n                finished[j - 1] = finished[j - 1] + 1\n                hold[j - 1, i - 1] = int(file[i][j])\n    finished = np.divide(np.round(np.divide(finished, total) * 1000), 10)\n    print(f'Completed: {\" \".join([f\"{f:g}%\" for f in finished])}')\n    print(f'Total: {np.round(np.divide(np.sum(finished), 3))}%')\n    input('Waiting: [enter]')\n\n    # file[1:] are all the rows\n    order = range(1, len(file))\n    random.shuffle(order)\n    # pick a random row\n    for row in order:\n        # check how many entries it has\n        current = num_rows(file[row])\n        if current <= 1:\n            # if less than 1, run the row\n            print('Check participant #' + file[row][0])\n            fname = os.getcwd() + '/abide/' + file[row][0]\n            if os.path.isfile(fname):\n                webbrowser.open('file://' + fname)\n                quality = input('Quality? [-1/0/1/e/c] ')\n                if quality == 'e':\n                    break\n                if quality == 'c':\n                    print('Current comment: ' + file[row][4])\n                    comment = input('Comment: ')\n                    if len(comment) > 0:\n                        file[row][4] = comment\n                    quality = input('Quality? [-1/0/1/e] ')\n                if quality == 'e':\n                    break\n                file[row][current] = quality\n            else:\n                print('File does not exist')\n\n    print('Writing file sinfo.csv')\n    outfile = open('sinfo.csv', 'wb')\n    csvwriter = csv.writer(outfile)\n    csvwriter.writerows(file)\n    print('Ending')\n\n\nif __name__ == '__main__':\n    main()\n    sys.exit(0)\n"
  },
  {
    "path": "mriqc/bin/messages.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nABIDE_SUBJECT_FETCHED = 'Successfully processed subject {subject_id} from site {site_name}'\nABIDE_TEMPORAL_WARNING = 'WARNING: Error deleting temporal files: {message}'\nBIDS_LABEL_MISSING = 'Participant label(s) not found in the BIDS root directory: {label}'\nBIDS_GROUP_SIZE = 'Group size should be at least 0 (i.e. all participants assigned to same group).'\nCLF_CAPTURED_WARNING = 'Captured warning ({category}): {message}'\nCLF_CLASSIFIER_MISSING = 'No training samples were given, and the --load-classifier option {info}.'\nCLF_SAVED_RESULTS = 'Results saved as {path}.'\nCLF_TRAIN_LOAD_ERROR = 'Errors ({n_errors}) loading training set: {errors}.'\nCLF_WRONG_PARAMETER_COUNT = 'Wrong number of parameters.'\nDFCHECK_CSV_CHANGED = 'Output CSV file changed one or more values.'\nDFCHECK_CSV_COLUMNS = 'Output CSV file changed number of columns.'\nDFCHECK_DIFFERENT_BITS = 'Dataset has different BIDS bits w.r.t. reference.'\nDFCHECK_DIFFERENT_LENGTH = \"\"\"\\\nInput datasets have different lengths (input={len_input}, \\\nreference={len_reference}).\n\"\"\"\nDFCHECK_IQMS_CORRELATED = 'All IQMs show a Pearson correlation >= 0.95.'\nDFCHECK_IQMS_UNDER_095 = 'IQMs with Pearson correlation < 0.95:\\n{iqms}'\nHASH_REPORT = '{sha} {file_name}'\nPLOT_REPORT_VERSION = 'mriqc version:\\t{version}'\nPLOT_WORK_MISSING = 'Work directory of a previous MRIQC run was not found.'\nSUBJECT_WRANGLER_DESCRIPTION = \"\"\"\\\nBIDS-Apps participants wrangler tool\n------------------------------------\n\nThis command arranges the participant labels in groups for computation, and checks that the \\\nrequested participants have the corresponding folder in the bids_dir.\\\n\"\"\"\nWEBAPI_GET = 'Sending GET to {address}.'\nWEBAPI_REPORT = 'There are {n_records} records in database.'\n"
  },
  {
    "path": "mriqc/bin/mriqcwebapi_test.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nfrom mriqc.bin import messages\n\n\ndef get_parser():\n    \"\"\"\n    Build parser object.\n    \"\"\"\n    from argparse import ArgumentParser, RawTextHelpFormatter\n\n    parser = ArgumentParser(\n        description='MRIQCWebAPI: Check entries.', formatter_class=RawTextHelpFormatter\n    )\n    parser.add_argument(\n        'modality',\n        action='store',\n        choices=['T1w', 'bold'],\n        help='number of expected items in the database',\n    )\n    parser.add_argument(\n        'expected',\n        action='store',\n        type=int,\n        help='number of expected items in the database',\n    )\n    parser.add_argument(\n        '--webapi-url',\n        action='store',\n        default='https://mriqc.nimh.nih.gov/api/v1/T1w',\n        type=str,\n        help='IP address where the MRIQC WebAPI is listening',\n    )\n    return parser\n\n\ndef main():\n    \"\"\"Entry point.\"\"\"\n    import logging\n\n    from requests import get\n\n    # Run parser\n    MRIQC_LOG = logging.getLogger(__name__)\n    opts = get_parser().parse_args()\n    get_log_message = messages.WEBAPI_GET.format(address=opts.webapi_url)\n    MRIQC_LOG.info(get_log_message)\n    response = get(opts.webapi_url, timeout=5).json()\n    n_records = response['_meta']['total']\n    response_log_message = messages.WEBAPI_REPORT.format(n_records=n_records)\n    MRIQC_LOG.info(response_log_message)\n    if opts.expected != n_records:\n        raise AssertionError\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "mriqc/bin/nib_hash.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nExtracts the sha hash of the contents of a nifti file.\n\"\"\"\n\nfrom argparse import ArgumentParser, RawTextHelpFormatter\nfrom hashlib import sha1\n\nimport nibabel as nb\n\nfrom mriqc.bin import messages\n\n\ndef get_parser() -> ArgumentParser:\n    \"\"\"\n    A trivial parser.\n\n    Returns\n    -------\n    ArgumentParser\n        nib_hash execution parser\n    \"\"\"\n\n    parser = ArgumentParser(\n        description='Compare two pandas dataframes.',\n        formatter_class=RawTextHelpFormatter,\n    )\n    parser.add_argument('input_file', action='store', help='input nifti file')\n    return parser\n\n\ndef get_hash(nii_file: str) -> str:\n    \"\"\"\n    Computes the sha1 hash for a given NIfTI format file.\n\n    Parameters\n    ----------\n    nii_file : str\n        Path to *nii* file\n\n    Returns\n    -------\n    str\n        SHA1 hash\n    \"\"\"\n    with nb.openers.ImageOpener(nii_file) as fobj:\n        return sha1(fobj.read()).hexdigest()\n\n\ndef main():\n    \"\"\"Entry point.\"\"\"\n    file_name = get_parser().parse_args().input_file\n    sha = get_hash(file_name)\n    message = messages.HASH_REPORT.format(sha=sha, file_name=file_name)\n    print(message)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "mriqc/bin/subject_wrangler.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"BIDS-Apps subject wrangler.\"\"\"\n\nimport glob\nimport os.path as op\nfrom argparse import ArgumentParser, RawTextHelpFormatter\nfrom random import shuffle\nfrom textwrap import dedent\n\nfrom mriqc import __version__\nfrom mriqc.bin import messages\n\nCOMMAND = '{exec} {bids_dir} {out_dir} participant --participant_label {labels} {work_dir} {arguments} {logfile}'  # noqa: E501\n\n\ndef main():\n    \"\"\"Entry point.\"\"\"\n    parser = ArgumentParser(\n        formatter_class=RawTextHelpFormatter,\n        description=dedent(messages.SUBJECT_WRANGLER_DESCRIPTION),\n    )\n\n    parser.add_argument(\n        '-v',\n        '--version',\n        action='version',\n        version=f'mriqc v{__version__}',\n    )\n\n    parser.add_argument(\n        'bids_dir',\n        action='store',\n        help='The directory with the input dataset formatted according to the BIDS standard.',\n    )\n    parser.add_argument(\n        'output_dir',\n        action='store',\n        help='The directory where the output files '\n        'should be stored. If you are running group level analysis '\n        'this folder should be prepopulated with the results of the'\n        'participant level analysis.',\n    )\n    parser.add_argument(\n        '--participant_label',\n        '--subject_list',\n        '-S',\n        action='store',\n        help='The label(s) of the participant(s) that should be analyzed. '\n        'The label corresponds to sub-<participant_label> from the '\n        'BIDS spec (so it does not include \"sub-\"). If this parameter '\n        'is not provided all subjects should be analyzed. Multiple '\n        'participants can be specified with a space separated list.',\n        nargs='*',\n    )\n    parser.add_argument(\n        '--group-size',\n        default=1,\n        action='store',\n        type=int,\n        help='Parallelize participants in groups.',\n    )\n    parser.add_argument(\n        '--no-randomize',\n        default=False,\n        action='store_true',\n        help='Do not randomize participants list before grouping.',\n    )\n    parser.add_argument(\n        '--log-groups',\n        default=False,\n        action='store_true',\n        help='Append logging output.',\n    )\n    parser.add_argument(\n        '--multiple-workdir',\n        default=False,\n        action='store_true',\n        help='Split work directories by jobs.',\n    )\n    parser.add_argument(\n        '--bids-app-name',\n        default='mriqc',\n        action='store',\n        help='BIDS app to call.',\n    )\n    parser.add_argument('--args', default='', action='store', help='Append arguments.')\n\n    opts = parser.parse_args()\n\n    # Build settings dict\n    bids_dir = op.abspath(opts.bids_dir)\n    subject_dirs = glob.glob(op.join(bids_dir, 'sub-*'))\n    all_subjects = sorted([op.basename(subj)[4:] for subj in subject_dirs])\n\n    subject_list = opts.participant_label\n    if subject_list is None or not subject_list:\n        subject_list = all_subjects\n    else:\n        # remove sub- prefix, get unique\n        for i, subj in enumerate(subject_list):\n            subject_list[i] = subj[4:] if subj.startswith('sub-') else subj\n\n        subject_list = sorted(set(subject_list))\n\n        if list(set(subject_list) - set(all_subjects)):\n            non_exist = list(set(subject_list) - set(all_subjects))\n            missing_label_error = messages.BIDS_LABEL_MISSING.format(label=' '.join(non_exist))\n            raise RuntimeError(missing_label_error)\n\n    if not opts.no_randomize:\n        shuffle(subject_list)\n\n    gsize = opts.group_size\n\n    if gsize < 0:\n        raise RuntimeError(messages.BIDS_GROUP_SIZE)\n    if gsize == 0:\n        gsize = len(subject_list)\n\n    j = i + gsize\n    groups = [subject_list[i:j] for i in range(0, len(subject_list), gsize)]\n\n    log_arg = '>> log/mriqc-{:04d}.log' if opts.log_groups else ''\n    workdir_arg = ' -w work/sjob-{:04d}' if opts.multiple_workdir else ''\n    for i, part_group in enumerate(groups):\n        kwargs = {\n            'exec': opts.bids_app_name,\n            'bids_dir': bids_dir,\n            'out_dir': opts.output_dir,\n            'labels': ' '.join(part_group),\n            'work_dir': workdir_arg.format(i),\n            'arguments': opts.args,\n            'logfile': log_arg.format(i),\n        }\n        print(COMMAND.format(**kwargs))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "mriqc/cli/__init__.py",
    "content": ""
  },
  {
    "path": "mriqc/cli/parser.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Parser.\"\"\"\n\nimport re\n\nfrom mriqc import config\n\n\ndef _parse_participant_labels(value):\n    \"\"\"\n    Drop ``sub-`` prefix of participant labels.\n\n    >>> _parse_participant_labels(\"s060\")\n    ['s060']\n    >>> _parse_participant_labels(\"sub-s060\")\n    ['s060']\n    >>> _parse_participant_labels(\"s060 sub-s050\")\n    ['s050', 's060']\n    >>> _parse_participant_labels(\"s060 sub-s060\")\n    ['s060']\n    >>> _parse_participant_labels(\"s060\\tsub-s060\")\n    ['s060']\n\n    \"\"\"\n    return sorted(\n        {re.sub(r'^sub-', '', item.strip()) for item in re.split(r'\\s+', f'{value}'.strip())}\n    )\n\n\ndef _build_parser():\n    \"\"\"Build parser object.\"\"\"\n    import sys\n    import warnings\n    from argparse import Action, ArgumentDefaultsHelpFormatter, ArgumentParser\n    from functools import partial\n    from pathlib import Path\n\n    from packaging.version import Version\n\n    from .version import check_latest, is_flagged\n\n    class DeprecateAction(Action):\n        def __call__(self, parser, namespace, values, option_string=None):\n            warnings.warn(\n                f'Argument {option_string} is deprecated and is *ignored*.',\n                stacklevel=2,\n            )\n            delattr(namespace, self.dest)\n\n    class ParticipantLabelAction(Action):\n        def __call__(self, parser, namespace, values, option_string=None):\n            setattr(namespace, self.dest, _parse_participant_labels(' '.join(values)))\n\n    def _path_exists(path, parser):\n        \"\"\"Ensure a given path exists.\"\"\"\n        if path is None or not Path(path).exists():\n            raise parser.error(f'Path does not exist: <{path}>.')\n        return Path(path).expanduser().absolute()\n\n    def _min_one(value, parser):\n        \"\"\"Ensure an argument is not lower than 1.\"\"\"\n        value = int(value)\n        if value < 1:\n            raise parser.error(\"Argument can't be less than one.\")\n        return value\n\n    def _to_gb(value):\n        scale = {'G': 1, 'T': 10**3, 'M': 1e-3, 'K': 1e-6, 'B': 1e-9}\n        digits = ''.join([c for c in value if c.isdigit()])\n        n_digits = len(digits)\n        units = value[n_digits:] or 'G'\n        return int(digits) * scale[units[0]]\n\n    def _bids_filter(value):\n        from json import loads\n\n        if value and Path(value).exists():\n            return loads(Path(value).read_text())\n\n    verstr = f'MRIQC v{config.environment.version}'\n    currentv = Version(config.environment.version)\n\n    parser = ArgumentParser(\n        description=f\"\"\"\\\nMRIQC {config.environment.version}\nAutomated Quality Control and visual reports for Quality Assessment of structural \\\n(T1w, T2w) and functional MRI of the brain.\n\n{config.DSA_MESSAGE}\"\"\",\n        formatter_class=ArgumentDefaultsHelpFormatter,\n    )\n    PathExists = partial(_path_exists, parser=parser)\n    PositiveInt = partial(_min_one, parser=parser)\n\n    # Arguments as specified by BIDS-Apps\n    # required, positional arguments\n    # IMPORTANT: they must go directly with the parser object\n    parser.add_argument(\n        'bids_dir',\n        action='store',\n        type=PathExists,\n        help='The root folder of a BIDS valid dataset (sub-XXXXX folders should '\n        'be found at the top level in this folder).',\n    )\n    parser.add_argument(\n        'output_dir',\n        action='store',\n        type=Path,\n        help='The directory where the output files '\n        'should be stored. If you are running group level analysis '\n        'this folder should be prepopulated with the results of the '\n        'participant level analysis.',\n    )\n    parser.add_argument(\n        'analysis_level',\n        action='store',\n        nargs='+',\n        help='Level of the analysis that will be performed. '\n        'Multiple participant level analyses can be run independently '\n        '(in parallel) using the same output_dir.',\n        choices=['participant', 'group'],\n    )\n\n    # optional arguments\n    parser.add_argument('--version', action='version', version=verstr)\n    parser.add_argument(\n        '-v',\n        '--verbose',\n        dest='verbose_count',\n        action='count',\n        default=0,\n        help='Increases log verbosity for each occurrence, debug level is -vvv.',\n    )\n\n    # TODO: add 'mouse', 'macaque', and other populations once the pipeline is working\n    parser.add_argument(\n        '--species',\n        action='store',\n        type=str,\n        default='human',\n        choices=['human', 'rat'],\n        help='Use appropriate template for population',\n    )\n\n    g_bids = parser.add_argument_group('Options for filtering BIDS queries')\n    g_bids.add_argument(\n        '--participant-label',\n        '--participant_label',\n        '--participant-labels',\n        '--participant_labels',\n        dest='participant_label',\n        action=ParticipantLabelAction,\n        nargs='+',\n        help='A space delimited list of participant identifiers or a single '\n        'identifier (the sub- prefix can be removed).',\n    )\n    g_bids.add_argument(\n        '--bids-filter-file',\n        action='store',\n        type=Path,\n        metavar='PATH',\n        help='a JSON file describing custom BIDS input filter using pybids '\n        '{<suffix>:{<entity>:<filter>,...},...} '\n        '(https://github.com/bids-standard/pybids/blob/master/src/bids/layout/config/bids.json)',\n    )\n    g_bids.add_argument(\n        '--session-id',\n        action='store',\n        nargs='*',\n        type=str,\n        help='Filter input dataset by session ID.',\n    )\n    g_bids.add_argument(\n        '--run-id',\n        action='store',\n        type=int,\n        nargs='*',\n        help='DEPRECATED - This argument will be disabled. Use ``--bids-filter-file`` instead.',\n    )\n    g_bids.add_argument(\n        '--task-id',\n        action='store',\n        nargs='*',\n        type=str,\n        help='Filter input dataset by task ID.',\n    )\n    g_bids.add_argument(\n        '-m',\n        '--modalities',\n        action='store',\n        choices=config.SUPPORTED_SUFFIXES,\n        default=config.SUPPORTED_SUFFIXES,\n        nargs='*',\n        help='Filter input dataset by MRI type.',\n    )\n    g_bids.add_argument('--dsname', type=str, help='A dataset name.')\n    g_bids.add_argument(\n        '--bids-database-dir',\n        metavar='PATH',\n        help='Path to an existing PyBIDS database folder, for faster indexing '\n        '(especially useful for large datasets).',\n    )\n    g_bids.add_argument(\n        '--bids-database-wipe',\n        action='store_true',\n        default=False,\n        help='Wipe out previously existing BIDS indexing caches, forcing re-indexing.',\n    )\n    g_bids.add_argument(\n        '--no-datalad-get',\n        action='store_false',\n        dest='datalad_get',\n        help='Disable attempting to get remote files in DataLad datasets.',\n    )\n\n    # General performance\n    g_perfm = parser.add_argument_group('Options to handle performance')\n    g_perfm.add_argument(\n        '--nprocs',\n        '--n_procs',\n        '--n_cpus',\n        '-n-cpus',\n        action='store',\n        type=PositiveInt,\n        help=\"\"\"\\\nMaximum number of simultaneously running parallel processes executed by *MRIQC* \\\n(e.g., several instances of ANTs' registration). \\\nHowever, when ``--nprocs`` is greater or equal to the ``--omp-nthreads`` option, \\\nit also sets the maximum number of threads that simultaneously running processes \\\nmay aggregate (meaning, with ``--nprocs 16 --omp-nthreads 8`` a maximum of two \\\n8-CPU-threaded processes will be running at a given time). \\\nUnder this mode of operation, ``--nprocs`` sets the maximum number of processors \\\nthat can be assigned work within an *MRIQC* job, which includes all the processors \\\nused by currently running single- and multi-threaded processes. \\\nIf ``None``, the number of CPUs available will be automatically assigned (which may \\\nnot be what you want in, e.g., shared systems like a HPC cluster.\"\"\",\n    )\n    g_perfm.add_argument(\n        '--omp-nthreads',\n        '--ants-nthreads',\n        action='store',\n        type=PositiveInt,\n        help=\"\"\"\\\nMaximum number of threads that multi-threaded processes executed by *MRIQC* \\\n(e.g., ANTs' registration) can use. \\\nIf ``None``, the number of CPUs available will be automatically assigned (which may \\\nnot be what you want in, e.g., shared systems like a HPC cluster.\"\"\",\n    )\n    g_perfm.add_argument(\n        '--mem',\n        '--mem_gb',\n        '--mem-gb',\n        dest='memory_gb',\n        action='store',\n        type=_to_gb,\n        help='Upper bound memory limit for MRIQC processes.',\n    )\n    g_perfm.add_argument(\n        '--testing',\n        dest='debug',\n        action='store_true',\n        default=False,\n        help='Use testing settings for a minimal footprint.',\n    )\n    g_perfm.add_argument(\n        '-f',\n        '--float32',\n        action='store_true',\n        default=True,\n        help=\"Cast the input data to float32 if it's represented in higher precision \"\n        '(saves space and improves performance).',\n    )\n    g_perfm.add_argument(\n        '--pdb',\n        dest='pdb',\n        action='store_true',\n        default=False,\n        help='Open Python debugger (pdb) on exceptions.',\n    )\n\n    # Control instruments\n    g_outputs = parser.add_argument_group('Instrumental options')\n    g_outputs.add_argument(\n        '-w',\n        '--work-dir',\n        action='store',\n        type=Path,\n        default=Path('work').absolute(),\n        help='Path where intermediate results should be stored.',\n    )\n    g_outputs.add_argument('--verbose-reports', default=False, action='store_true')\n    g_outputs.add_argument('--reports-only', default=False, action='store_true')\n    g_outputs.add_argument(\n        '--write-graph',\n        action='store_true',\n        default=False,\n        help='Write workflow graph.',\n    )\n    g_outputs.add_argument(\n        '--dry-run',\n        action='store_true',\n        default=False,\n        help='Do not run the workflow.',\n    )\n    g_outputs.add_argument(\n        '--resource-monitor',\n        '--profile',\n        dest='resource_monitor',\n        action='store_true',\n        default=False,\n        help='Hook up the resource profiler callback to nipype.',\n    )\n    g_outputs.add_argument(\n        '--use-plugin',\n        action='store',\n        default=None,\n        type=Path,\n        help='Nipype plugin configuration file.',\n    )\n    g_outputs.add_argument(\n        '--crashfile-format',\n        action='store',\n        default='txt',\n        choices=['txt', 'pklz'],\n        type=str,\n        help='Nipype crashfile format',\n    )\n    g_outputs.add_argument(\n        '--no-sub',\n        default=False,\n        action='store_true',\n        help=\"Turn off submission of anonymized quality metrics to MRIQC's metrics repository.\",\n    )\n    g_outputs.add_argument(\n        '--email',\n        action='store',\n        default='',\n        type=str,\n        help='Email address to include with quality metric submission.',\n    )\n\n    g_outputs.add_argument(\n        '--webapi-url',\n        action='store',\n        type=str,\n        help='IP address where the MRIQC WebAPI is listening.',\n    )\n    g_outputs.add_argument(\n        '--webapi-port',\n        action='store',\n        type=int,\n        help='Port where the MRIQC WebAPI is listening.',\n    )\n\n    g_outputs.add_argument(\n        '--upload-strict',\n        action='store_true',\n        default=False,\n        help='Upload will fail if upload is strict.',\n    )\n    g_outputs.add_argument(\n        '--notrack',\n        action='store_true',\n        help='Opt-out of sending tracking information of this run to the NiPreps developers. This'\n        ' information helps to improve MRIQC and provides an indicator of real world usage '\n        ' crucial for obtaining funding.',\n    )\n\n    # ANTs options\n    g_ants = parser.add_argument_group('Specific settings for ANTs')\n    g_ants.add_argument(\n        '--ants-float',\n        action='store_true',\n        default=False,\n        help='Use float number precision on ANTs computations.',\n    )\n    g_ants.add_argument(\n        '--ants-settings',\n        action='store',\n        help='Path to JSON file with settings for ANTs.',\n    )\n\n    # Diffusion workflow settings\n    g_dwi = parser.add_argument_group('Diffusion MRI workflow configuration')\n    g_dwi.add_argument(\n        '--min-dwi-length',\n        action='store',\n        default=config.workflow.min_len_dwi,\n        dest='min_len_dwi',\n        help='Drop DWI runs with fewer orientations than this threshold.',\n        type=int,\n    )\n\n    # Functional workflow settings\n    g_func = parser.add_argument_group('Functional MRI workflow configuration')\n    g_func.add_argument(\n        '--min-bold-length',\n        action='store',\n        default=config.workflow.min_len_bold,\n        dest='min_len_bold',\n        help='Drop BOLD runs with fewer time points than this threshold.',\n        type=int,\n    )\n    g_func.add_argument(\n        '--fft-spikes-detector',\n        action='store_true',\n        default=False,\n        help='Turn on FFT based spike detector (slow).',\n    )\n    g_func.add_argument(\n        '--fd_thres',\n        action='store',\n        default=0.2,\n        type=float,\n        help='Threshold on framewise displacement estimates to detect outliers.',\n    )\n    g_func.add_argument(\n        '--deoblique',\n        action='store_true',\n        default=False,\n        help='Deoblique the functional scans during head motion correction preprocessing.',\n    )\n    g_func.add_argument(\n        '--despike',\n        action='store_true',\n        default=False,\n        help='Despike the functional scans during head motion correction preprocessing.',\n    )\n    g_func.add_argument(\n        '--start-idx',\n        action=DeprecateAction,\n        type=int,\n        help='DEPRECATED Initial volume in functional timeseries that should be '\n        'considered for preprocessing.',\n    )\n    g_func.add_argument(\n        '--stop-idx',\n        action=DeprecateAction,\n        type=int,\n        help='DEPRECATED Final volume in functional timeseries that should be '\n        'considered for preprocessing.',\n    )\n\n    latest = check_latest()\n    if latest is not None and currentv < latest:\n        print(\n            f\"\"\"\\\nYou are using MRIQC v{currentv}, and a newer version is available: {latest}.\"\"\",\n            file=sys.stderr,\n        )\n\n    _blist = is_flagged()\n    if _blist[0]:\n        _reason = _blist[1] or 'unknown'\n        print(\n            f\"\"\"\\\nWARNING: This version of MRIQC ({config.environment.version}) has been FLAGGED\n(reason: {_reason}).\nThat means some severe flaw was found in it and we strongly \\\ndiscourage its usage.\"\"\",\n            file=sys.stderr,\n        )\n\n    return parser\n\n\ndef parse_args(args=None, namespace=None):\n    \"\"\"Parse args and run further checks on the command line.\"\"\"\n    from json import loads\n    from logging import DEBUG, FileHandler\n    from pathlib import Path\n    from pprint import pformat\n\n    from niworkflows.utils.bids import DEFAULT_BIDS_QUERIES, collect_data\n\n    from mriqc import __version__, data\n    from mriqc._warnings import DATE_FMT, LOGGER_FMT, _LogFormatter\n    from mriqc.messages import PARTICIPANT_START\n    from mriqc.utils.misc import initialize_meta_and_data\n\n    parser = _build_parser()\n    opts = parser.parse_args(args, namespace)\n    config.execution.log_level = int(max(25 - 5 * opts.verbose_count, DEBUG))\n\n    config.loggers.init()\n\n    _log_file = Path(opts.output_dir) / 'logs' / f'mriqc-{config.execution.run_uuid}.log'\n    _log_file.parent.mkdir(exist_ok=True, parents=True)\n    _handler = FileHandler(_log_file)\n    _handler.setFormatter(\n        _LogFormatter(\n            fmt=LOGGER_FMT.format(color='', reset=''),\n            datefmt=DATE_FMT,\n            colored=False,\n        )\n    )\n    config.loggers.default.addHandler(_handler)\n\n    extra_messages = ['']\n\n    if opts.bids_filter_file:\n        extra_messages.insert(\n            0,\n            f'  * BIDS filters-file: {opts.bids_filter_file.absolute()}.',\n        )\n\n    notice_path = data.load.readable('NOTICE')\n    config.loggers.cli.log(\n        26,\n        PARTICIPANT_START.format(\n            version=__version__,\n            bids_dir=opts.bids_dir,\n            output_dir=opts.output_dir,\n            analysis_level=opts.analysis_level,\n            notice='\\n  '.join(\n                ['NOTICE'] + notice_path.read_text().splitlines(keepends=False)[1:]\n            ),\n            extra_messages='\\n'.join(extra_messages),\n        ),\n    )\n    config.from_dict(vars(opts))\n\n    # Load base plugin_settings from file if --use-plugin\n    if opts.use_plugin is not None:\n        from yaml import safe_load as loadyml\n\n        with open(opts.use_plugin) as f:\n            plugin_settings = loadyml(f)\n        _plugin = plugin_settings.get('plugin')\n        if _plugin:\n            config.nipype.plugin = _plugin\n            config.nipype.plugin_args = plugin_settings.get('plugin_args', {})\n            config.nipype.nprocs = config.nipype.plugin_args.get('nprocs', config.nipype.nprocs)\n\n    # Load BIDS filters\n    if opts.bids_filter_file:\n        config.execution.bids_filters = {\n            k.lower(): v for k, v in loads(opts.bids_filter_file.read_text()).items()\n        }\n\n    bids_dir = config.execution.bids_dir\n    output_dir = config.execution.output_dir\n    work_dir = config.execution.work_dir\n    version = config.environment.version\n\n    # Ensure input and output folders are not the same\n    if output_dir == bids_dir:\n        parser.error(\n            'The selected output folder is the same as the input BIDS folder. '\n            f'Please modify the output path (suggestion: {bids_dir}).'\n            / 'derivatives'\n            / ('mriqc-{}'.format(version.split('+')[0]))\n        )\n\n    if bids_dir in work_dir.parents:\n        parser.error(\n            'The selected working directory is a subdirectory of the input BIDS folder. '\n            'Please modify the output path.'\n        )\n\n    config.execution.bids_dir_datalad = (\n        config.execution.datalad_get\n        and (bids_dir / '.git').exists()\n        and (bids_dir / '.datalad').exists()\n    )\n\n    # Setup directories\n    config.execution.log_dir = output_dir / 'logs'\n    # Check and create output and working directories\n    config.execution.log_dir.mkdir(exist_ok=True, parents=True)\n    output_dir.mkdir(exist_ok=True, parents=True)\n    work_dir.mkdir(exist_ok=True, parents=True)\n\n    # Force initialization of the BIDSLayout\n    config.execution.init()\n\n    participant_label = [\n        d.name[4:] for d in config.execution.bids_dir.glob('sub-*') if d.is_dir() and d.exists()\n    ]\n\n    if config.execution.participant_label is not None:\n        selected_label = set(config.execution.participant_label)\n        if missing_subjects := selected_label - set(participant_label):\n            parser.error(\n                'One or more participant labels were not found in the BIDS directory: '\n                f'{\", \".join(missing_subjects)}.'\n            )\n        participant_label = selected_label\n\n    config.execution.participant_label = sorted(participant_label)\n\n    # Handle analysis_level\n    analysis_level = set(config.workflow.analysis_level)\n    if not config.execution.participant_label:\n        analysis_level.add('group')\n    config.workflow.analysis_level = list(analysis_level)\n\n    # List of files to be run\n    lc_modalities = [mod.lower() for mod in config.execution.modalities]\n    bids_dataset, _ = collect_data(\n        config.execution.layout,\n        config.execution.participant_label,\n        session_id=config.execution.session_id,\n        task=config.execution.task_id,\n        group_echos=True,\n        bids_filters={mod: config.execution.bids_filters.get(mod, {}) for mod in lc_modalities},\n        queries={mod: DEFAULT_BIDS_QUERIES[mod] for mod in lc_modalities},\n    )\n\n    # Drop empty queries\n    bids_dataset = {mod: files for mod, files in bids_dataset.items() if files}\n    config.workflow.inputs = bids_dataset\n\n    # Check the query is not empty\n    if not list(config.workflow.inputs.values()):\n        ffile = (\n            '(--bids-filter-file was not set)'\n            if not opts.bids_filter_file\n            else f\"(with '--bids-filter-file {opts.bids_filter_file}')\"\n        )\n        parser.error(\n            f\"\"\"\\\nQuerying BIDS dataset at <{config.execution.bids_dir}> got an empty result.\nPlease, check out your currently set filters {ffile}:\n{pformat(config.execution.bids_filters, indent=2, width=99)}\"\"\"\n        )\n\n    # Check no DWI or others are sneaked into MRIQC\n    unknown_mods = set(config.workflow.inputs.keys()) - {\n        suffix.lower() for suffix in config.SUPPORTED_SUFFIXES\n    }\n    if unknown_mods:\n        parser.error(\n            f'MRIQC is unable to process the following modalities: {\", \".join(unknown_mods)}.'\n        )\n\n    initialize_meta_and_data()\n\n    # set specifics for alternative populations\n    if opts.species.lower() != 'human':\n        config.workflow.species = opts.species\n        # TODO: add other species once rats are working\n        if opts.species.lower() == 'rat':\n            config.workflow.template_id = 'Fischer344'\n            # mean distance from the lateral edge to the center of the brain is\n            # ~ PA:10 mm, LR:7.5 mm, and IS:5 mm (see DOI: 10.1089/089771503770802853)\n            # roll movement is most likely to occur, so set to 7.5 mm\n            config.workflow.fd_radius = 7.5\n            # block uploads for the moment; can be reversed before wider release\n            config.execution.no_sub = True\n"
  },
  {
    "path": "mriqc/cli/run.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Definition of the command line interface's (CLI) entry point.\"\"\"\n\n\ndef format_elapsed_time(elapsed_timedelta):\n    \"\"\"Format a timedelta instance as a %Hh %Mmin %Ss string.\"\"\"\n    return (\n        f'{elapsed_timedelta.days * 24 + elapsed_timedelta.seconds // 3600:02d}h '\n        f'{(elapsed_timedelta.seconds % 3600) // 60:02d}min '\n        f'{elapsed_timedelta.seconds % 60:02d}s'\n    )\n\n\ndef main(argv=None):\n    \"\"\"Entry point for MRIQC's CLI.\"\"\"\n    import atexit\n    import datetime\n    import gc\n    import os\n    import sys\n    import time\n    from tempfile import mkstemp\n\n    from mriqc import config, messages\n    from mriqc.cli.parser import parse_args\n\n    atexit.register(config.restore_env)\n\n    config.settings.start_time = time.time()\n\n    # Run parser\n    parse_args(argv)\n\n    if config.execution.pdb:\n        from mriqc.utils.debug import setup_exceptionhook\n\n        setup_exceptionhook()\n        config.nipype.plugin = 'Linear'\n\n    # CRITICAL Save the config to a file. This is necessary because the execution graph\n    # is built as a separate process to keep the memory footprint low. The most\n    # straightforward way to communicate with the child process is via the filesystem.\n    # The config file name needs to be unique, otherwise multiple mriqc instances\n    # will create write conflicts.\n    config_file = config.to_filename()\n    config.loggers.cli.info(f'MRIQC config file: {config_file}.')\n\n    exitcode = 0\n    # Set up participant level\n    if 'participant' in config.workflow.analysis_level:\n        _pool = None\n        if config.nipype.plugin in ('MultiProc', 'LegacyMultiProc'):\n            import multiprocessing as mp\n            import multiprocessing.forkserver\n            from concurrent.futures import ProcessPoolExecutor\n            from contextlib import suppress\n\n            os.environ['OMP_NUM_THREADS'] = '1'\n            os.environ['NUMEXPR_MAX_THREADS'] = '1'\n\n            with suppress(RuntimeError):\n                mp.set_start_method('fork')\n            gc.collect()\n\n            _pool = ProcessPoolExecutor(\n                max_workers=config.nipype.nprocs,\n                initializer=config._process_initializer,\n                initargs=(config_file,),\n            )\n\n        _resmon = None\n        if config.execution.resource_monitor:\n            from mriqc.instrumentation.resources import ResourceRecorder\n\n            _resmon = ResourceRecorder(\n                pid=os.getpid(),\n                log_file=mkstemp(\n                    dir=config.execution.work_dir, prefix='.resources.', suffix='.tsv'\n                )[1],\n            )\n            _resmon.start()\n\n        if not config.execution.notrack:\n            from ..utils.telemetry import setup_migas\n\n            setup_migas()\n\n        # CRITICAL Call build_workflow(config_file, retval) in a subprocess.\n        # Because Python on Linux does not ever free virtual memory (VM), running the\n        # workflow construction jailed within a process preempts excessive VM buildup.\n        from multiprocessing import Manager, Process\n\n        with Manager() as mgr:\n            from .workflow import build_workflow\n\n            retval = mgr.dict()\n            p = Process(target=build_workflow, args=(str(config_file), retval))\n            p.start()\n            p.join()\n\n            mriqc_wf = retval.get('workflow', None)\n            exitcode = p.exitcode or retval.get('return_code', 0)\n\n        # CRITICAL Load the config from the file. This is necessary because the ``build_workflow``\n        # function executed constrained in a process may change the config (and thus the global\n        # state of MRIQC).\n        config.load(config_file)\n\n        exitcode = exitcode or (mriqc_wf is None) * os.EX_SOFTWARE\n        if exitcode != 0:\n            sys.exit(exitcode)\n\n        # Initialize nipype config\n        config.nipype.init()\n        # Make sure loggers are started\n        config.loggers.init()\n\n        if _resmon:\n            config.loggers.cli.info(f'Started resource recording at {_resmon._logfile}.')\n\n        # Resource management options\n        if config.nipype.plugin in ('MultiProc', 'LegacyMultiProc') and (\n            1 < config.nipype.nprocs < config.nipype.omp_nthreads\n        ):\n            config.loggers.cli.warning(\n                'Per-process threads (--omp-nthreads=%d) exceed total '\n                'threads (--nthreads/--n_cpus=%d)',\n                config.nipype.omp_nthreads,\n                config.nipype.nprocs,\n            )\n\n        # Check synthstrip is properly installed\n        if not config.environment.synthstrip_path:\n            config.loggers.cli.warning(\n                (\n                    'Please make sure FreeSurfer is installed and the FREESURFER_HOME '\n                    'environment variable is defined and pointing at the right directory.'\n                )\n                if config.environment.freesurfer_home is None\n                else (\n                    f'FreeSurfer seems to be installed at {config.environment.freesurfer_home},'\n                    \" however SynthStrip's model is not found at the expected path.\"\n                )\n            )\n\n        if mriqc_wf is None:\n            sys.exit(os.EX_SOFTWARE)\n\n        if mriqc_wf and config.execution.write_graph:\n            mriqc_wf.write_graph(graph2use='colored', format='svg', simple_form=True)\n\n        if not config.execution.dry_run and not config.execution.reports_only:\n            # Warn about submitting measures BEFORE\n            if not config.execution.no_sub:\n                config.loggers.cli.warning(config.DSA_MESSAGE)\n\n            # Clean up master process before running workflow, which may create forks\n            gc.collect()\n            # run MRIQC\n            _plugin = config.nipype.get_plugin()\n            if _pool:\n                from mriqc.engine.plugin import MultiProcPlugin\n\n                _plugin = {\n                    'plugin': MultiProcPlugin(pool=_pool, plugin_args=config.nipype.plugin_args),\n                }\n            mriqc_wf.run(**_plugin)\n\n            # Warn about submitting measures AFTER\n            if not config.execution.no_sub:\n                config.loggers.cli.warning(config.DSA_MESSAGE)\n\n        if not config.execution.dry_run:\n            from mriqc.reports.individual import generate_reports\n\n            generate_reports()\n\n        _subject_duration = (time.time() - config.settings.start_time) / sum(\n            len(files) for files in config.workflow.inputs.values()\n        )\n        _subject_duration_td = datetime.timedelta(seconds=_subject_duration)\n        time_strf = format_elapsed_time(_subject_duration_td)\n\n        config.loggers.cli.log(\n            25,\n            messages.PARTICIPANT_FINISHED.format(duration=time_strf),\n        )\n\n        if _resmon is not None:\n            from mriqc.instrumentation.viz import plot\n\n            _resmon.stop()\n            plot(\n                _resmon._logfile,\n                param='mem_rss_mb',\n                out_file=str(_resmon._logfile).replace('.tsv', '.rss.png'),\n            )\n            plot(\n                _resmon._logfile,\n                param='mem_vsm_mb',\n                out_file=str(_resmon._logfile).replace('.tsv', '.vsm.png'),\n            )\n\n    # Set up group level\n    if 'group' in config.workflow.analysis_level:\n        from mriqc.reports.group import gen_html as group_html\n\n        from ..utils.misc import generate_tsv  # , generate_pred\n\n        config.loggers.cli.log(26, messages.GROUP_START)\n\n        # Generate reports\n        mod_group_reports = []\n        for mod in config.execution.modalities or config.SUPPORTED_SUFFIXES:\n            output_dir = config.execution.output_dir\n            dataframe, out_tsv = generate_tsv(output_dir, mod)\n            # If there are no iqm.json files, nothing to do.\n            if dataframe is None:\n                continue\n\n            tsv_message = messages.TSV_GENERATED.format(modality=mod, path=out_tsv)\n            config.loggers.cli.info(tsv_message)\n\n            # out_pred = generate_pred(derivatives_dir, settings['output_dir'], mod)\n            # if out_pred is not None:\n            #     log.info('Predicted QA CSV table for the %s data generated (%s)',\n            #                    mod, out_pred)\n\n            out_html = output_dir / f'group_{mod}.html'\n            group_html(\n                out_tsv,\n                mod,\n                csv_failed=output_dir / f'group_variant-failed_{mod}.csv',\n                out_file=out_html,\n            )\n            report_message = messages.GROUP_REPORT_GENERATED.format(modality=mod, path=out_html)\n            config.loggers.cli.info(report_message)\n            mod_group_reports.append(mod)\n\n        if not mod_group_reports:\n            raise Exception(messages.GROUP_NO_DATA)\n\n        config.loggers.cli.info(messages.GROUP_FINISHED)\n\n    from mriqc.utils.bids import write_bidsignore, write_derivative_description\n\n    config.loggers.cli.info(messages.BIDS_META)\n    write_derivative_description(config.execution.bids_dir, config.execution.output_dir)\n    write_bidsignore(config.execution.output_dir)\n\n    _run_duration = time.time() - config.settings.start_time\n    _run_duration_td = datetime.timedelta(seconds=_run_duration)\n    time_strf = format_elapsed_time(_run_duration_td)\n\n    config.loggers.cli.log(\n        26,\n        messages.RUN_FINISHED.format(duration=time_strf),\n    )\n    config.to_filename(\n        config.execution.log_dir / f'config-{config.execution.run_uuid}.toml',\n        store_inputs=False,  # Inputs are not necessary anymore\n    )\n    sys.exit(exitcode)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "mriqc/cli/version.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Version CLI helpers.\"\"\"\n\nfrom contextlib import suppress\nfrom datetime import datetime, timezone\n\nimport requests\n\nfrom mriqc import __version__, config\n\nRELEASE_EXPIRY_DAYS = 14\nDATE_FMT = '%Y%m%d'\nUTC = timezone.utc\n\n\ndef check_latest():\n    \"\"\"Determine whether this is the latest version.\"\"\"\n    from packaging.version import InvalidVersion, Version\n\n    latest = None\n    date = None\n    outdated = None\n    cachefile = config.environment.cache_path / 'latest'\n    try:\n        cachefile.parent.mkdir(parents=True, exist_ok=True)\n    except OSError:\n        cachefile = None\n\n    if cachefile and cachefile.exists():\n        with suppress(Exception):\n            latest, date = cachefile.read_text().split('|')\n\n        if latest and date:\n            try:\n                latest = Version(latest)\n                date = datetime.strptime(date, DATE_FMT).astimezone(UTC)\n            except (InvalidVersion, ValueError):\n                latest = None\n            else:\n                if abs((datetime.now(tz=UTC) - date).days) > RELEASE_EXPIRY_DAYS:\n                    outdated = True\n\n    if latest is None or outdated is True:\n        response = None\n\n        with suppress(Exception):\n            response = requests.get(url='https://pypi.org/pypi/mriqc/json', timeout=1.0)\n\n        if response and response.status_code == 200:\n            versions = [Version(rel) for rel in response.json()['releases'].keys()]\n            versions = [rel for rel in versions if not rel.is_prerelease]\n            if versions:\n                latest = sorted(versions)[-1]\n        else:\n            latest = None\n\n    if cachefile is not None and latest is not None:\n        with suppress(Exception):\n            cachefile.write_text('|'.join((f'{latest}', datetime.now(tz=UTC).strftime(DATE_FMT))))\n\n    return latest\n\n\ndef is_flagged():\n    \"\"\"Check whether current version is flagged.\"\"\"\n    # https://raw.githubusercontent.com/nipreps/mriqc/master/.versions.json\n    flagged = ()\n    response = None\n    with suppress(Exception):\n        response = requests.get(\n            url=\"\"\"\\\nhttps://raw.githubusercontent.com/nipreps/mriqc/master/.versions.json\"\"\",\n            timeout=1.0,\n        )\n\n    if response and response.status_code == 200:\n        flagged = response.json().get('flagged', {}) or {}\n\n    if __version__ in flagged:\n        return True, flagged[__version__]\n\n    return False, None\n"
  },
  {
    "path": "mriqc/cli/workflow.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nThe workflow builder factory method.\n\nAll the checks and the construction of the workflow are done\ninside this function that has pickleable inputs and output\ndictionary (``retval``) to allow isolation using a\n``multiprocessing.Process`` that allows dmriprep to enforce\na hard-limited memory-scope.\n\"\"\"\n\n\ndef build_workflow(config_file, retval):\n    \"\"\"Create the Nipype Workflow that supports the whole execution graph.\"\"\"\n    import os\n\n    from mriqc import config\n\n    # We do not need OMP > 1 for workflow creation\n    os.environ['OMP_NUM_THREADS'] = '1'\n    os.environ['NUMEXPR_MAX_THREADS'] = '1'\n\n    from mriqc.workflows.core import init_mriqc_wf\n\n    config.load(config_file)\n    # Initialize nipype config\n    config.nipype.init()\n    # Make sure loggers are started\n    config.loggers.init()\n\n    retval['return_code'] = 1\n    retval['workflow'] = None\n\n    config.loggers.cli.log(25, \"Building MRIQC's workflows...\")\n    retval['workflow'] = init_mriqc_wf()\n    retval['return_code'] = int(retval['workflow'] is None)\n    config.loggers.cli.log(25, f'Workflow building finished (exit code {retval[\"return_code\"]}).')\n    return retval\n"
  },
  {
    "path": "mriqc/config.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nr\"\"\"\nA Python module to maintain unique, run-wide *MRIQC* settings.\n\nThis module implements the memory structures to keep a consistent, singleton config.\nSettings are passed across processes via filesystem, and a copy of the settings for\neach run and subject is left under\n``<output_dir>/sub-<participant_id>/log/<run_unique_id>/mriqc.toml``.\nSettings are stored using :abbr:`ToML (Tom's Markup Language)`.\nThe module has a :py:func:`~mriqc.config.to_filename` function to allow writing out\nthe settings to hard disk in *ToML* format, which looks like:\n\n.. literalinclude:: ../mriqc/data/config-example.toml\n   :language: toml\n   :name: mriqc.toml\n   :caption: **Example file representation of MRIQC settings**.\n\nThis config file is used to pass the settings across processes,\nusing the :py:func:`~mriqc.config.load` function.\n\nConfiguration sections\n----------------------\n.. autoclass:: environment\n   :members:\n.. autoclass:: execution\n   :members:\n.. autoclass:: workflow\n   :members:\n.. autoclass:: nipype\n   :members:\n\nUsage\n-----\nA config file is used to pass settings and collect information as the execution\ngraph is built across processes.\n\n.. code-block:: Python\n\n    from mriqc import config\n    config_file = mktemp(dir=config.execution.work_dir, prefix='.mriqc.', suffix='.toml')\n    config.to_filename(config_file)\n    # Call build_workflow(config_file, retval) in a subprocess\n    with Manager() as mgr:\n        from .workflow import build_workflow\n        retval = mgr.dict()\n        p = Process(target=build_workflow, args=(str(config_file), retval))\n        p.start()\n        p.join()\n    config.load(config_file)\n    # Access configs from any code section as:\n    value = config.section.setting\n\nLogging\n-------\n.. autoclass:: loggers\n   :members:\n\nOther responsibilities\n----------------------\nThe :py:mod:`config` is responsible for other conveniency actions.\n\n  * Switching Python's :obj:`multiprocessing` to *forkserver* mode.\n  * Set up a filter for warnings as early as possible.\n  * Automated I/O magic operations. Some conversions need to happen in the\n    store/load processes (e.g., from/to :obj:`~pathlib.Path` \\<-\\> :obj:`str`,\n    :py:class:`~bids.layout.BIDSLayout`, etc.)\n\n\"\"\"\n\nfrom __future__ import annotations\n\nimport os\nimport pickle\nimport sys\nfrom collections.abc import Iterable\nfrom contextlib import suppress\nfrom pathlib import Path\nfrom time import strftime\nfrom typing import TYPE_CHECKING, Any\nfrom uuid import uuid4\n\ntry:\n    # This option is only available with Python 3.8\n    from importlib.metadata import version as get_version\nexcept ImportError:\n    from importlib_metadata import version as get_version\n\n# Ignore annoying warnings\nfrom mriqc._warnings import logging\n\nif TYPE_CHECKING:\n    from re import Pattern\n\n    from bids.layout import BIDSLayout\n\n__version__: str = get_version('mriqc')\n_pre_exec_env: dict[str, str] = dict(os.environ)\n\n# Reduce numpy's vms by limiting OMP_NUM_THREADS\n_default_omp_threads: int = int(os.getenv('OMP_NUM_THREADS', os.cpu_count()))\n\n# Disable NiPype etelemetry always\n_disable_et: bool = bool(os.getenv('NO_ET') is not None or os.getenv('NIPYPE_NO_ET') is not None)\nos.environ['NIPYPE_NO_ET'] = '1'\nos.environ['NO_ET'] = '1'\n\n_mriqc_dev: bool = os.getenv('MRIQC_DEV', '0').lower() in ('1', 'on', 'true', 'y', 'yes')\n\nif not hasattr(sys, '_is_pytest_session'):\n    sys._is_pytest_session = False  # Trick to avoid sklearn's FutureWarnings\n# Disable all warnings in main and children processes only on production versions\nif not any(\n    (\n        '+' in __version__,\n        __version__.endswith('.dirty'),\n        _mriqc_dev,\n    )\n):\n    os.environ['PYTHONWARNINGS'] = 'ignore'\n\n\nSUPPORTED_SUFFIXES: tuple[str, ...] = ('T1w', 'T2w', 'bold', 'dwi')\n\nDEFAULT_MEMORY_MIN_GB: float = 0.01\nDSA_MESSAGE: str = \"\"\"\\\nIMPORTANT: Anonymized quality metrics (IQMs) will be submitted to MRIQC's metrics \\\nrepository. \\\nSubmission of IQMs can be disabled using the ``--no-sub`` argument. \\\nPlease visit https://mriqc.readthedocs.io/en/latest/dsa.html to revise MRIQC's \\\nData Sharing Agreement.\"\"\"\n\n_exec_env: str = os.name\n_docker_ver: str | None = None\n# special variable set in the container\nif os.getenv('IS_DOCKER_8395080871'):\n    _exec_env: str = 'singularity'\n    _cgroup: Path = Path('/proc/1/cgroup')\n    if _cgroup.exists() and 'docker' in _cgroup.read_text():\n        _docker_ver = os.getenv('DOCKER_VERSION_8395080871')\n        _exec_env = 'docker'\n    del _cgroup\n\n_templateflow_home: Path = Path(\n    os.getenv(\n        'TEMPLATEFLOW_HOME',\n        os.path.join(os.getenv('HOME'), '.cache', 'templateflow'),\n    )\n)\n\n_free_mem_at_start: int | None = None\nwith suppress(Exception):\n    from psutil import virtual_memory\n\n    _free_mem_at_start: int = round(virtual_memory().free / 1024**3, 1)\n\n_oc_limit: str = 'n/a'\n_oc_policy: str = 'n/a'\nwith suppress(Exception):\n    # Memory policy may have a large effect on types of errors experienced\n    _proc_oc_path: Path = Path('/proc/sys/vm/overcommit_memory')\n    if _proc_oc_path.exists():\n        _oc_policy: str = {'0': 'heuristic', '1': 'always', '2': 'never'}.get(\n            _proc_oc_path.read_text().strip(), 'unknown'\n        )\n        if _oc_policy != 'never':\n            _proc_oc_kbytes: Path = Path('/proc/sys/vm/overcommit_kbytes')\n            if _proc_oc_kbytes.exists():\n                _oc_limit: str = _proc_oc_kbytes.read_text().strip()\n            if _oc_limit in ('0', 'n/a') and Path('/proc/sys/vm/overcommit_ratio').exists():\n                _oc_limit: str = '{}%'.format(\n                    Path('/proc/sys/vm/overcommit_ratio').read_text().strip()\n                )\n\n_memory_gb: float | None = None\n\nif 'linux' in sys.platform:\n    with suppress(Exception):\n        with open('/proc/meminfo') as f_in:\n            _meminfo_lines: list[str] = f_in.readlines()\n            _mem_total_line: str = [line for line in _meminfo_lines if 'MemTotal' in line][0]\n            _mem_total: float = float(_mem_total_line.split()[1])\n            _memory_gb = _mem_total / (1024.0**2)\nelif 'darwin' in sys.platform:\n    from shutil import which\n    from subprocess import check_output\n\n    if _cmd := which('sysctl'):\n        with suppress(Exception):\n            _mem_str: str = check_output([_cmd, 'hw.memsize']).decode().strip().split(' ')[-1]\n            _memory_gb: float = float(_mem_str) / (1024.0**3)\n\n# Check for FreeSurfer's SynthStrip model\n_fs_home: str | None = os.getenv('FREESURFER_HOME', None)\n_default_model_path: Path | None = (\n    Path(_fs_home) / 'models' / 'synthstrip.1.pt' if _fs_home else None\n)\n\nif _fs_home and not _default_model_path.exists():\n    _default_model_path = None\n\n# Override the unique ID if MRIQC_DEV is set\n_run_uuid: str = (\n    '{}_{}'.format(strftime('%Y%m%d-%H%M%S'), uuid4())\n    if not _mriqc_dev\n    else '18480913-163000_PhineasG-ageh-adhi-sacc-ident9b1ab0f'\n)\n\n_mriqc_cache: Path = Path(\n    os.getenv('MRIQC_CACHE_PATH', str(Path.home() / '.cache' / 'mriqc'))\n).absolute()\n\n\nclass _Config:\n    \"\"\"An abstract class forbidding instantiation.\"\"\"\n\n    _paths: tuple[str, ...] = ()\n    _hidden: tuple[str, ...] = ()\n\n    def __init__(self):\n        \"\"\"Avert instantiation.\"\"\"\n        raise RuntimeError('Configuration type is not instantiable.')\n\n    @classmethod\n    def load(cls, sections, init=True):\n        \"\"\"Store settings from a dictionary.\"\"\"\n        for k, v in sections.items():\n            if v is None:\n                continue\n            if k in cls._paths:\n                setattr(cls, k, Path(v).absolute())\n                continue\n            if hasattr(cls, k):\n                setattr(cls, k, v)\n\n        if init:\n            try:\n                cls.init()\n            except AttributeError:\n                pass\n\n    @classmethod\n    def get(cls) -> dict[str, Any]:\n        \"\"\"Return defined settings.\"\"\"\n        out: dict[str, str] = {}\n        for k, v in cls.__dict__.items():\n            if k.startswith('_') or v is None:\n                continue\n            if k in cls._hidden:\n                continue\n            if callable(getattr(cls, k)):\n                continue\n            if k in cls._paths:\n                v = str(v)\n            out[k] = v\n        return out\n\n\nclass settings(_Config):\n    \"\"\"Settings of this config module.\"\"\"\n\n    file_path: Path | None = None\n    \"\"\"Path to this configuration file.\"\"\"\n    start_time: float | None = None\n    \"\"\"A :obj:`~time.time` timestamp at the time the workflow is started.\"\"\"\n\n    _paths: tuple[str, ...] = ('file_path',)\n\n\nclass environment(_Config):\n    \"\"\"\n    Read-only options regarding the platform and environment.\n\n    Crawls runtime descriptive settings (e.g., default FreeSurfer license,\n    execution environment, nipype and *MRIQC* versions, etc.).\n    The ``environment`` section is not loaded in from file,\n    only written out when settings are exported.\n    This config section is useful when reporting issues,\n    and these variables are tracked whenever the user does not\n    opt-out using the ``--notrack`` argument.\n\n    \"\"\"\n\n    cache_path: Path = _mriqc_cache\n    \"\"\"Path to the location of the cache directory.\"\"\"\n    cpu_count: int = os.cpu_count()\n    \"\"\"Number of available CPUs.\"\"\"\n    exec_docker_version: str | None = _docker_ver\n    \"\"\"Version of Docker Engine.\"\"\"\n    exec_env: str = _exec_env\n    \"\"\"A string representing the execution platform.\"\"\"\n    free_mem: int | None = _free_mem_at_start\n    \"\"\"Free memory at start.\"\"\"\n    freesurfer_home: str | None = _fs_home\n    \"\"\"Path to the *FreeSurfer* installation (from ``FREESURFER_HOME`` environment variable).\"\"\"\n    overcommit_policy: str = _oc_policy\n    \"\"\"Linux's kernel virtual memory overcommit policy.\"\"\"\n    overcommit_limit: str = _oc_limit\n    \"\"\"Linux's kernel virtual memory overcommit limits.\"\"\"\n    nipype_version: str = get_version('nipype')\n    \"\"\"Nipype's current version.\"\"\"\n    synthstrip_path: Path | None = _default_model_path\n    \"\"\"Path to *SynthStrip*'s model weights (requires *FreeSurfer*).\"\"\"\n    templateflow_version: str = get_version('templateflow')\n    \"\"\"The TemplateFlow client version installed.\"\"\"\n    total_memory: float | None = _memory_gb\n    \"\"\"Total memory available, in GB.\"\"\"\n    version: str = __version__\n    \"\"\"*MRIQC*'s version.\"\"\"\n    _pre_mriqc: dict[str, str] = _pre_exec_env\n    \"\"\"Environment variables before MRIQC's execution.\"\"\"\n\n\nclass nipype(_Config):\n    \"\"\"Nipype settings.\"\"\"\n\n    crashfile_format: str = 'txt'\n    \"\"\"The file format for crashfiles, either text or pickle.\"\"\"\n    get_linked_libs: bool = False\n    \"\"\"Run NiPype's tool to enlist linked libraries for every interface.\"\"\"\n    local_hash_check: bool = True\n    \"\"\"Check if interface is cached locally before executing.\"\"\"\n    memory_gb: float | str | None = None\n    \"\"\"Estimation in GB of the RAM this workflow can allocate at any given time.\"\"\"\n    nprocs: int = os.cpu_count()\n    \"\"\"Number of processes (compute tasks) that can be run in parallel (multiprocessing only).\"\"\"\n    omp_nthreads: int = _default_omp_threads\n    \"\"\"Number of CPUs a single process can access for multithreaded execution.\"\"\"\n    plugin: str = 'MultiProc'\n    \"\"\"NiPype's execution plugin.\"\"\"\n    plugin_args: dict[str, Any] = {\n        'maxtasksperchild': 1,\n        'raise_insufficient': False,\n    }\n    \"\"\"Settings for NiPype's execution plugin.\"\"\"\n    remove_node_directories: bool = False\n    \"\"\"Remove directories whose outputs have already been used up.\"\"\"\n    resource_monitor: bool = False\n    \"\"\"Enable resource monitor.\"\"\"\n    stop_on_first_crash: bool = True\n    \"\"\"Whether the workflow should stop or continue after the first error.\"\"\"\n\n    @classmethod\n    def get_plugin(cls) -> dict[str, Any]:\n        \"\"\"Format a dictionary for Nipype consumption.\"\"\"\n        out: dict[str, Any] = {\n            'plugin': cls.plugin,\n            'plugin_args': cls.plugin_args,\n        }\n        if cls.plugin in ('MultiProc', 'LegacyMultiProc'):\n            out['plugin_args']['n_procs'] = int(cls.nprocs)\n            if cls.memory_gb:\n                out['plugin_args']['memory_gb'] = float(cls.memory_gb)\n        return out\n\n    @classmethod\n    def init(cls) -> None:\n        \"\"\"Set NiPype configurations.\"\"\"\n        from nipype import config as ncfg\n\n        # Nipype config (logs and execution)\n        ncfg.update_config(\n            {\n                'execution': {\n                    'crashdump_dir': str(execution.log_dir),\n                    'crashfile_format': cls.crashfile_format,\n                    'get_linked_libs': cls.get_linked_libs,\n                    'stop_on_first_crash': cls.stop_on_first_crash,\n                }\n            }\n        )\n\n\nclass execution(_Config):\n    \"\"\"Configure run-level settings.\"\"\"\n\n    ants_float: bool = False\n    \"\"\"Use float number precision for ANTs computations.\"\"\"\n    bids_dir: str | os.PathLike | None = None\n    \"\"\"An existing path to the dataset, which must be BIDS-compliant.\"\"\"\n    bids_dir_datalad: bool = False\n    \"\"\"Whether the input directory seems under DataLad's version control.\"\"\"\n    bids_database_dir: str | os.PathLike | None = None\n    \"\"\"Path to the directory containing SQLite database indices for the input BIDS dataset.\"\"\"\n    bids_database_wipe: bool = False\n    \"\"\"Wipe out previously existing BIDS indexing caches, forcing re-indexing.\"\"\"\n    bids_description_hash: str | None = None\n    \"\"\"Checksum (SHA256) of the ``dataset_description.json`` of the BIDS dataset.\"\"\"\n    bids_filters: dict | None = None\n    \"\"\"A dictionary describing custom BIDS input filter using PyBIDS.\"\"\"\n    cwd: str = os.getcwd()\n    \"\"\"Current working directory.\"\"\"\n    datalad_get: bool = True\n    \"\"\"\n    If :obj:`~mriqc.config.execution.bids_dir` is a DataLad dataset, a ``datalad get`` command\n    will be executed to ensure all remote files have been fetched.\n    This behavior can be disabled with the command line option ``--no-datalad-get``.\n    \"\"\"\n    debug: bool = False\n    \"\"\"Run in sloppy mode (meaning, suboptimal parameters that minimize run-time).\"\"\"\n    dry_run: bool = False\n    \"\"\"Just test, do not run.\"\"\"\n    dsname: str = '<unset>'\n    \"\"\"A dataset name used when generating files from the rating widget.\"\"\"\n    echo_id: str | None = None\n    \"\"\"Select a particular echo for multi-echo EPI datasets.\"\"\"\n    float32: bool = True\n    \"\"\"Cast the input data to float32 if it's represented with higher precision.\"\"\"\n    layout: BIDSLayout | None = None\n    \"\"\"A :py:class:`~bids.layout.BIDSLayout` object, see :py:func:`init`.\"\"\"\n    log_dir: str | os.PathLike | None = None\n    \"\"\"The path to a directory that contains execution logs.\"\"\"\n    log_level: int | str = 25\n    \"\"\"Output verbosity.\"\"\"\n    modalities: Iterable[str] = None\n    \"\"\"Filter input dataset by MRI type.\"\"\"\n    no_sub: bool = False\n    \"\"\"Turn off submission of anonymized quality metrics to Web API.\"\"\"\n    notrack: bool = False\n    \"\"\"Disable the sharing of usage information with developers.\"\"\"\n    output_dir: str | os.PathLike | None = None\n    \"\"\"Folder where derivatives will be stored.\"\"\"\n    participant_label: Iterable[str] | None = None\n    \"\"\"List of participant identifiers that are to be preprocessed.\"\"\"\n    pdb: bool = False\n    \"\"\"Drop into PDB when exceptions are encountered.\"\"\"\n    reports_only: bool = False\n    \"\"\"Only build the reports, based on the reportlets found in a cached working directory.\"\"\"\n    resource_monitor: bool = False\n    \"\"\"Enable resource monitor.\"\"\"\n    run_id: str | None = None\n    \"\"\"Filter input dataset by run identifier.\"\"\"\n    run_uuid: str = _run_uuid\n    \"\"\"Unique identifier of this particular run.\"\"\"\n    session_id: str | None = None\n    \"\"\"Filter input dataset by session identifier.\"\"\"\n    task_id: str | None = None\n    \"\"\"Select a particular task from all available in the dataset.\"\"\"\n    templateflow_home: str | os.PathLike | None = _templateflow_home\n    \"\"\"The root folder of the TemplateFlow client.\"\"\"\n    upload_strict: bool = False\n    \"\"\"Workflow will crash if upload is not successful.\"\"\"\n    verbose_reports: bool = False\n    \"\"\"Generate extended reports.\"\"\"\n    webapi_token: str = '<secret_token>'\n    \"\"\"Authorization token for the WebAPI service.\"\"\"\n    webapi_url: str = 'https://mriqc.nimh.nih.gov:443/api/v1'\n    \"\"\"IP address where the MRIQC WebAPI is listening.\"\"\"\n    work_dir: Path = Path('work').absolute()\n    \"\"\"Path to a working directory where intermediate results will be available.\"\"\"\n    write_graph: bool = False\n    \"\"\"Write out the computational graph corresponding to the planned preprocessing.\"\"\"\n\n    _layout: BIDSLayout | None = None\n\n    _paths: tuple[str, ...] = (\n        'anat_derivatives',\n        'bids_dir',\n        'bids_database_dir',\n        'fs_license_file',\n        'fs_subjects_dir',\n        'log_dir',\n        'output_dir',\n        'templateflow_home',\n        'work_dir',\n    )\n\n    _hidden: tuple[str, ...] = ('webapi_token',)\n\n    @classmethod\n    def init(cls) -> None:\n        \"\"\"Create a new BIDS Layout accessible with :attr:`~execution.layout`.\"\"\"\n\n        if cls.bids_filters is None:\n            cls.bids_filters = {}\n\n        # Process --run-id if the argument was provided\n        if cls.run_id:\n            for mod in cls.modalities:\n                cls.bids_filters.setdefault(mod.lower(), {})['run'] = cls.run_id\n\n        if cls._layout is None:\n            import re\n\n            from bids.layout import BIDSLayout\n            from bids.layout.index import BIDSLayoutIndexer\n\n            ignore_paths: list[Pattern] = [\n                # Ignore folders at the top if they don't start with /sub-<label>/\n                re.compile(r'^(?!/sub-[a-zA-Z0-9]+)'),\n                # Ignore all modality subfolders, except for func/ or anat/\n                re.compile(\n                    r'^/sub-[a-zA-Z0-9]+(/ses-[a-zA-Z0-9]+)?/'\n                    r'(beh|fmap|pet|perf|meg|eeg|ieeg|micr|nirs)'\n                ),\n                # Ignore all files, except for the supported modalities\n                re.compile(r'^.+(?<!(_T1w|_T2w|bold|_dwi))\\.(json|nii|nii\\.gz)$'),\n            ]\n\n            if cls.participant_label:\n                # If we know participant labels, ignore all other\n                ignore_paths[0] = re.compile(\n                    r'^(?!/sub-(' + '|'.join(cls.participant_label) + '))'\n                )\n\n            # Recommended after PyBIDS 12.1\n            _indexer: BIDSLayoutIndexer = BIDSLayoutIndexer(\n                validate=False,\n                ignore=ignore_paths,\n            )\n\n            # Initialize database in a multiprocessing-safe manner\n            _db_path: Path = (\n                cls.work_dir if cls.participant_label else cls.output_dir\n            ) / f'.bids_db-{cls.run_uuid}'\n\n            if cls.bids_database_dir is None:\n                cls.bids_database_dir = (\n                    cls.output_dir / '.bids_db' if not cls.participant_label else _db_path\n                )\n\n            if cls.bids_database_wipe or not cls.bids_database_dir.exists():\n                _db_path.mkdir(exist_ok=True, parents=True)\n\n                cls._layout = BIDSLayout(\n                    str(cls.bids_dir),\n                    database_path=_db_path,\n                    indexer=_indexer,\n                )\n\n                if _db_path != cls.bids_database_dir:\n                    _db_path.replace(cls.bids_database_dir.absolute())\n\n            cls._layout = BIDSLayout(\n                str(cls.bids_dir),\n                database_path=cls.bids_database_dir,\n                indexer=_indexer,\n            )\n\n            # Rewrite __repr__ to avoid the layout query and build the summary\n            # For a smallish dataset this takes one minute each time.\n            # See https://github.com/nipreps/mriqc/issues/1239\n            cls._layout.__class__.__repr__ = lambda x: f'BIDS Layout: {cls.bids_dir}'\n\n        cls.layout: BIDSLayout = cls._layout\n\n\n# These variables are not necessary anymore\ndel _exec_env\ndel _templateflow_home\ndel _free_mem_at_start\ndel _oc_limit\ndel _oc_policy\n\n\nclass workflow(_Config):\n    \"\"\"Configure the particular execution graph of this workflow.\"\"\"\n\n    analysis_level: list[str] = ['participant']\n    \"\"\"Level of analysis.\"\"\"\n    biggest_file_gb: dict[int] = 1\n    \"\"\"Dictionary holding the size of largest file in GB (per modality).\"\"\"\n    deoblique: bool = False\n    \"\"\"Deoblique the functional scans during head motion correction preprocessing.\"\"\"\n    despike: bool = False\n    \"\"\"Despike the functional scans during head motion correction preprocessing.\"\"\"\n    fd_thres: float = 0.2\n    \"\"\"Threshold on Framewise Displacement estimates to detect outliers.\"\"\"\n    fd_radius: int = 50\n    \"\"\"Radius in mm. of the sphere for the FD calculation.\"\"\"\n    fft_spikes_detector: bool = False\n    \"\"\"Turn on FFT based spike detector (slow).\"\"\"\n    inputs: list[str | os.PathLike] | None = None\n    \"\"\"List of files to be processed with MRIQC.\"\"\"\n    inputs_entities: dict[list[dict]]\n    \"\"\"List of entities corresponding to inputs.\"\"\"\n    inputs_metadata: dict[list[dict | list[dict]]] | None = None\n    \"\"\"List of metadata corresponding to inputs.\"\"\"\n    inputs_path: Path | None = None\n    \"\"\"Path to a pickle file with the input paths and metadata.\"\"\"\n    min_len_dwi: int = 7\n    \"\"\"\n    Minimum DWI length to be considered a \"processable\" dataset\n    (default: 7, assuming one low-b and six gradients for diffusion tensor imaging).\n    \"\"\"\n    min_len_bold: int = 5\n    \"\"\"Minimum BOLD length to be considered a \"processable\" dataset.\"\"\"\n    species: str = 'human'\n    \"\"\"Subject species to choose most appropriate template\"\"\"\n    template_id: str = 'MNI152NLin2009cAsym'\n    \"\"\"TemplateFlow ID of template used for the anatomical processing.\"\"\"\n\n    _hidden: tuple[str, ...] = ('inputs', 'inputs_entities', 'inputs_metadata')\n\n    @classmethod\n    def init(cls) -> None:\n        if cls.inputs_path is None:\n            cls.inputs_path = execution.work_dir / f'inputs-{execution.run_uuid}.pkl'\n\n        if cls.inputs_path.exists():\n            with open(cls.inputs_path, 'rb') as handle:\n                _inputs = pickle.load(handle)  # noqa: S301\n\n                cls.inputs = _inputs['paths']\n                cls.inputs_metadata = _inputs['metadata']\n                cls.inputs_entities = _inputs['entities']\n\n\nclass loggers:\n    \"\"\"Keep loggers easily accessible (see :py:func:`init`).\"\"\"\n\n    _datefmt: str = '%y%m%d %H:%M:%S'\n    _init: bool = False\n    _other_loggers: tuple[str, ...] = ('py.warnings', 'numexpr.utils', 'datalad')\n\n    default: logging.Logger = logging.getLogger()\n    \"\"\"The root logger.\"\"\"\n    cli: logging.Logger = logging.getLogger('mriqc')\n    \"\"\"Command-line interface logging.\"\"\"\n    workflow: logging.Logger | None = None\n    \"\"\"NiPype's workflow logger.\"\"\"\n    interface: logging.Logger | None = None\n    \"\"\"NiPype's interface logger.\"\"\"\n    utils: logging.Logger | None = None\n    \"\"\"NiPype's utils logger.\"\"\"\n\n    @classmethod\n    def init(cls) -> None:\n        \"\"\"\n        Set the log level, initialize all loggers into :py:class:`loggers`.\n\n            * Add new logger levels (25: IMPORTANT, and 15: VERBOSE).\n            * Add a new sub-logger (``cli``).\n            * Logger configuration.\n\n        \"\"\"\n        if not cls._init:\n            from nipype import config as ncfg\n            from nipype import logging as nlogging\n\n            cls.workflow = nlogging.getLogger('nipype.workflow')\n            cls.interface = nlogging.getLogger('nipype.interface')\n            cls.utils = nlogging.getLogger('nipype.utils')\n\n            cls.workflow.handlers.clear()\n            cls.interface.handlers.clear()\n            cls.utils.handlers.clear()\n\n            ncfg.update_config(\n                {\n                    'logging': {\n                        'log_directory': str(execution.log_dir),\n                        'log_to_file': True,\n                    },\n                }\n            )\n\n            for name in cls._other_loggers:\n                _thislogger: logging.Logger = logging.getLogger(name)\n                _thislogger.setLevel(logging.WARNING)\n\n            cls._init = True\n\n        cls.default.setLevel(execution.log_level)\n        cls.cli.setLevel(execution.log_level)\n        cls.interface.setLevel(execution.log_level)\n        cls.workflow.setLevel(execution.log_level)\n        cls.utils.setLevel(execution.log_level)\n\n    @classmethod\n    def getLogger(cls, name) -> logging.Logger:\n        \"\"\"Create a new logger.\"\"\"\n        retval: logging.Logger | None = getattr(cls, name)\n        if retval is None:\n            setattr(cls, name, logging.getLogger(name))\n            retval.setLevel(execution.log_level)\n        return retval\n\n\ndef from_dict(sections: dict) -> None:\n    \"\"\"Read settings from a flat dictionary.\"\"\"\n    execution.load(sections)\n    workflow.load(sections)\n    nipype.load(sections, init=False)\n\n\ndef load(filename: str | os.PathLike) -> None:\n    \"\"\"Load settings from file.\"\"\"\n    try:\n        from tomllib import loads\n    except ModuleNotFoundError:  # Python < 3.11\n        from tomli import loads\n\n    filename: Path = Path(filename)\n    sections: dict[str, Any] = loads(filename.read_text())\n    for sectionname, configs in sections.items():\n        if sectionname != 'environment':\n            section = getattr(sys.modules[__name__], sectionname)\n            section.load(configs)\n\n    if settings.file_path is None:\n        settings.file_path = filename\n\n    loggers.cli.debug(f'Loaded MRIQC config file: {settings.file_path}.')\n\n\ndef get(flat: bool = False) -> dict[str, dict[str, Any]]:\n    \"\"\"Get config as a dict.\"\"\"\n    sections = {\n        'environment': environment.get(),\n        'execution': execution.get(),\n        'workflow': workflow.get(),\n        'nipype': nipype.get(),\n        'settings': settings.get(),\n    }\n    if not flat:\n        return sections\n\n    return {\n        '.'.join((section, k)): v\n        for section, configs in sections.items()\n        for k, v in configs.items()\n    }\n\n\ndef dumps() -> str:\n    \"\"\"Format config into toml.\"\"\"\n    from toml import dumps\n\n    return dumps(get())\n\n\ndef to_filename(\n    filename: str | os.PathLike | None = None,\n    store_inputs: bool = True,\n) -> Path:\n    \"\"\"Write settings to file.\"\"\"\n\n    if filename:\n        settings.file_path = Path(filename)\n    elif settings.file_path is None:\n        settings.file_path = execution.work_dir / f'config-{execution.run_uuid}.toml'\n\n    settings.file_path.parent.mkdir(exist_ok=True, parents=True)\n    settings.file_path.write_text(dumps())\n    loggers.cli.debug(f'Saved MRIQC config file: {settings.file_path}.')\n\n    if store_inputs:\n        if workflow.inputs_path is None:\n            workflow.inputs_path = execution.work_dir / f'inputs-{execution.run_uuid}.pkl'\n\n        # Pickle inputs\n        with open(workflow.inputs_path, 'wb') as handle:\n            inputs_dict = {\n                'paths': workflow.inputs,\n                'metadata': workflow.inputs_metadata,\n                'entities': workflow.inputs_entities,\n            }\n            pickle.dump(inputs_dict, handle, protocol=pickle.HIGHEST_PROTOCOL)\n\n        loggers.cli.debug(f'Saved MRIQC inputs file: {workflow.inputs_path}.')\n    return settings.file_path\n\n\ndef _process_initializer(config_file: Path) -> None:\n    \"\"\"Initialize the environment of the child process.\"\"\"\n    from mriqc import config\n\n    # Disable eTelemetry\n    os.environ['NIPYPE_NO_ET'] = '1'\n    os.environ['NO_ET'] = '1'\n\n    # Load config\n    config.load(config_file)\n\n    # Initialize nipype config\n    config.nipype.init()\n\n    # Make sure loggers are started\n    config.loggers.init()\n\n    # Change working directory according to the config\n    os.chdir(config.execution.cwd)\n\n    # Set the maximal number of threads per process\n    os.environ['OMP_NUM_THREADS'] = f'{config.nipype.omp_nthreads}'\n    os.environ['NUMEXPR_MAX_THREADS'] = f'{config.nipype.omp_nthreads}'\n\n\ndef restore_env() -> None:\n    \"\"\"Restore the original environment.\"\"\"\n\n    for k in os.environ.keys():\n        del os.environ[k]\n\n    for k, v in environment._pre_mriqc.items():\n        os.environ[k] = v\n"
  },
  {
    "path": "mriqc/conftest.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"py.test configuration\"\"\"\n\nimport os\nimport tempfile\nfrom pathlib import Path\nfrom sys import version_info\n\nimport nibabel as nb\nimport numpy as np\nimport pandas as pd\nimport pytest\n\n# disable ET\nos.environ['NO_ET'] = '1'\n\n_datadir = (Path(__file__).parent / 'data' / 'tests').resolve(strict=True)\nniprepsdev_path = os.getenv('TEST_DATA_HOME', str(Path.home() / '.cache' / 'mriqc'))\ntest_output_dir = os.getenv('TEST_OUTPUT_DIR')\ntest_workdir = os.getenv('TEST_WORK_DIR')\n\n\n@pytest.fixture(autouse=True)\ndef expand_namespace(doctest_namespace):\n    doctest_namespace['PY_VERSION'] = version_info\n    doctest_namespace['np'] = np\n    doctest_namespace['nb'] = nb\n    doctest_namespace['pd'] = pd\n    doctest_namespace['os'] = os\n    doctest_namespace['pytest'] = pytest\n    doctest_namespace['Path'] = Path\n    doctest_namespace['testdata_path'] = _datadir\n    doctest_namespace['niprepsdev_path'] = niprepsdev_path\n\n    doctest_namespace['os'] = os\n    doctest_namespace['Path'] = Path\n\n    tmpdir = tempfile.TemporaryDirectory()\n    doctest_namespace['tmpdir'] = tmpdir.name\n\n    doctest_namespace['output_dir'] = (\n        Path(test_output_dir) if test_output_dir is not None else Path(tmpdir.name)\n    )\n\n    cwd = os.getcwd()\n    os.chdir(tmpdir.name)\n    yield\n    os.chdir(cwd)\n    tmpdir.cleanup()\n\n\n@pytest.fixture\ndef testdata_path():\n    return _datadir\n\n\n@pytest.fixture\ndef workdir():\n    return None if test_workdir is None else Path(test_workdir)\n\n\n@pytest.fixture\ndef outdir():\n    return None if test_output_dir is None else Path(test_output_dir)\n"
  },
  {
    "path": "mriqc/data/NOTICE",
    "content": "MRIQC\nCopyright © The NiPreps Developers.\n\nThis product includes software developed by\nthe NiPreps Community (https://nipreps.org/).\n\nPortions of this software were developed at the Department of\nPsychology at Stanford University, Stanford, CA, US.\n\nThis software contains code ultimately derived from the\nPCP Quality Assessment Protocol (QAP;\nhttp://preprocessed-connectomes-project.org/quality-assessment-protocol)\nby C. Craddock, S. Giavasis, D. Clark, Z. Shezhad, and J. Pellman.\n\nThis software is also distributed as a Docker container image.\nThe bootstrapping file for the image (\"Dockerfile\") is licensed\nunder the MIT License.\n"
  },
  {
    "path": "mriqc/data/__init__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2024 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nMRIQC data files\n\n.. autofunction:: load\n\n.. automethod:: load.readable\n\n.. automethod:: load.as_path\n\n.. automethod:: load.cached\n\"\"\"\n\nfrom acres import Loader\n\nload = Loader(__package__)\n"
  },
  {
    "path": "mriqc/data/bootstrap-anat.yml",
    "content": "# Copyright 2023 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n###########################################################################\n# Reports bootstrap file\n# ======================\n# This is a YAML-formatted file specifying how the NiReports assembler\n# will search for \"reportlets\" and compose them into a report file,\n# typically in HTML format.\n###########################################################################\n\npackagename: mriqc\ntitle: '{filename} :: Anatomical MRI report'\nsections:\n- name: Summary\n  reportlets:\n  - bids: {datatype: figures, desc: summary, extension: [.html]}\n- name: Basic visual report\n  reportlets:\n  - bids: {datatype: figures, desc: background}\n    caption: This panel shows a mosaic enhancing the background around the head.\n      Artifacts usually unveil themselves in the air surrounding the head, where no signal\n      sources are present.\n    subtitle: View of the background of the anatomical image\n  - bids: {datatype: figures, desc: zoomed}\n    caption: This panel shows a mosaic of the brain. This mosaic is the most suitable to\n      screen head-motion intensity inhomogeneities, global/local noise, signal leakage\n      (for example, from the eyeballs and across the phase-encoding axis), etc.\n    subtitle: Zoomed-in mosaic view of the brain\n- name: Extended visual report\n  reportlets:\n  - bids: {datatype: figures, desc: airmask}\n    caption: The <em>hat</em>-mask calculated internally by MRIQC. Some metrics will use this\n      mask, for instance, to find out artifacts and estimate the spread of gaussian noise\n      added to the signal. This mask leaves out the air around the face to avoid measuring\n      noise sourcing from the eyeballs and their movement.\n    subtitle: '&laquo;Hat&raquo;-mask'\n  - bids: {datatype: figures, desc: noisefit}\n    caption: The noise fit internally estimated by MRIQC to calculate the QI<sub>1</sub> index\n      proposed by <a href=\"https://doi.org/10.1002/mrm.21992\" target=\"_blank\">Mortamet et al. (2009)</a>.\n    subtitle: Distribution of the noise within the <em>hat</em> mask\n    style:\n      max-width: 450px\n  - bids: {datatype: figures, desc: artifacts}\n    caption: Mask of artifactual intensities identified within the <em>hat</em>-mask.\n    subtitle: Artifactual intensities on the background\n  - bids: {datatype: figures, desc: brainmask}\n    caption: Brain mask as internally extracted by MRIQC. Defects on the brainmask could\n      indicate problematic aspects of the image quality-wise.\n    subtitle: Brain extraction performance\n  - bids: {datatype: figures, desc: head}\n    caption: A mask of the head calculated internally by MRIQC.\n    subtitle: Head mask\n  - bids: {datatype: figures, desc: segmentation}\n    caption: Brain tissue segmentation, as internally extracted by MRIQC.\n      Defects on this segmentation, as well as noisy tissue labels could\n      indicate problematic aspects of the image quality-wise.\n    subtitle: Brain tissue segmentation\n  - bids: {datatype: figures, desc: norm}\n    caption: This panel shows a <em>quick-and-dirty</em> nonlinear registration into\n      the <code>MNI152NLin2009cAsym</code> template accessed with\n      <a href=\"https://templateflow.org/browse\" target=\"_blank\"><em>TemplateFlow</em></a>.\n    subtitle: Spatial normalization of the anatomical image\n    static: false\n\n- name: About\n  nested: true\n  reportlets:\n  - custom: errors\n    path: '{reportlets_dir}/{run_uuid}'\n    captions: <em>MRIQC</em> may have recorded failure conditions.\n    title: Errors\n  - metadata: \"input\"\n    settings:\n      # By default, only the first dictionary will be expanded.\n      # If folded is true, all will be folded. If false all expanded.\n      folded: true\n      # If an ID is not provided, one should be generated automatically\n      id: 'about-metadata'\n    caption: |\n      Thanks for using <em>MRIQC</em>. The following information may assist in\n      reconstructing the provenance of the corresponding derivatives.\n    title: Reproducibility and provenance information\n\n# Rating widget\nplugins:\n- module: nireports.assembler\n  path: data/rating-widget/bootstrap.yml\n"
  },
  {
    "path": "mriqc/data/bootstrap-dwi.yml",
    "content": "# Copyright 2023 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n###########################################################################\n# Reports bootstrap file\n# ======================\n# This is a YAML-formatted file specifying how the NiReports assembler\n# will search for \"reportlets\" and compose them into a report file,\n# typically in HTML format.\n###########################################################################\n\npackagename: mriqc\ntitle: '{filename} :: Diffusion MRI MRIQC report'\nsections:\n- name: Summary\n  reportlets:\n  - bids: {datatype: figures, desc: summary, extension: [.html]}\n  - bids: {datatype: figures, desc: heatmap}\n    caption: This visualization divides the data by shells, and shows the joint distribution\n      of SNR vs. FA. At the bottom, the distributions are marginalized for SNR.\n      Please note that the figures of SNR provided are calculated with a coarse estimation of\n      the signal variability, and therefore should be interpreted with care.\n    subtitle: Shell-wise joint distribution of SNR vs. FA in every voxel\n  - bids: {datatype: figures, desc: fa}\n    caption: Reconstructed FA map.\n    subtitle: Fractional anisotropy (FA) map\n  - bids: {datatype: figures, desc: md}\n    caption: Reconstructed MD map.\n    subtitle: Mean diffusivity (MD) map\n- name: DWI shells\n  ordering: bval\n  reportlets:\n  - bids: {datatype: figures, desc: avgstd}\n    caption: This panel shows mosaics flickering between the voxel-wise average and standard deviation\n      for each shell.\n    subtitle: Voxel-wise average and standard deviation across volumes in this <em>DWI shell</em>.\n    static: false\n  - bids: {datatype: figures, desc: background}\n    caption: This panel shows a mosaic enhancing the background around the head.\n      Artifacts usually unveil themselves in the air surrounding the head, where no signal\n      sources are present.\n    subtitle: View of the background of the voxel-wise average of this <em>DWI shell</em>\n\n- name: About\n  nested: true\n  reportlets:\n  - custom: errors\n    path: '{reportlets_dir}/{run_uuid}'\n    captions: <em>MRIQC</em> may have recorded failure conditions.\n    title: Errors\n  - metadata: \"input\"\n    settings:\n      # By default, only the first dictionary will be expanded.\n      # If folded is true, all will be folded. If false all expanded.\n      folded: true\n      # If an ID is not provided, one should be generated automatically\n      id: 'about-metadata'\n    caption: |\n      Thanks for using <em>MRIQC</em>. The following information may assist in\n      reconstructing the provenance of the corresponding derivatives.\n    title: Reproducibility and provenance information\n\n# Rating widget\nplugins:\n- module: nireports.assembler\n  path: data/rating-widget/bootstrap.yml\n"
  },
  {
    "path": "mriqc/data/bootstrap-func.yml",
    "content": "# Copyright 2023 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n###########################################################################\n# Reports bootstrap file\n# ======================\n# This is a YAML-formatted file specifying how the NiReports assembler\n# will search for \"reportlets\" and compose them into a report file,\n# typically in HTML format.\n###########################################################################\n\npackagename: mriqc\ntitle: \"{filename} :: MRIQC's BOLD fMRI report\"\nsections:\n- name: Summary\n  reportlets:\n  - bids: {datatype: figures, desc: summary, extension: [.html]}\n- name: Basic echo-wise reports\n  ordering: echo\n  reportlets:\n  - bids: {datatype: figures, desc: stdev}\n    subtitle: Standard deviation of signal through time\n    caption: The voxel-wise standard deviation of the signal (variability along time).\n  - bids: {datatype: figures, desc: background}\n    caption: This panel shows a mosaic enhancing the background around the head.\n      Artifacts usually unveil themselves in the air surrounding the head, where no signal\n      sources are present.\n    subtitle: View of the background of the voxel-wise average of the BOLD timeseries\n  - bids: {datatype: figures, desc: zoomed}\n    caption: This panel shows a mosaic of the brain. This mosaic is the most suitable to\n      screen head-motion intensity inhomogeneities, global/local noise, signal leakage\n      (for example, from the eyeballs and across the phase-encoding axis), etc.\n    subtitle: Voxel-wise average of BOLD time-series, zoomed-in covering just the brain\n  - bids: {datatype: figures, desc: carpet}\n    subtitle: Carpetplot and nuisance signals\n    caption: The so-called &laquo;carpetplot&raquo; may assist in assessing head-motion\n      derived artifacts and respiation effects.\n\n- name: Extended echo-wise reports\n  ordering: echo\n  reportlets:\n  - bids: {datatype: figures, desc: mean}\n    subtitle: Voxel-wise average of BOLD time-series\n    caption: The average signal calculated across the last axis (time).\n\n- name: Extended reports shared across echos\n  reportlets:\n  - bids: {datatype: figures, desc: brainmask}\n    caption: Brain mask as internally extracted by MRIQC. Defects on the brainmask could\n      indicate problematic aspects of the image quality-wise.\n    subtitle: Brain extraction performance\n  - bids: {datatype: figures, desc: norm}\n    caption: This panel shows a <em>quick-and-dirty</em> nonlinear registration into\n      the <code>MNI152NLin2009cAsym</code> template accessed with\n      <a href=\"https://templateflow.org/browse\" target=\"_blank\"><em>TemplateFlow</em></a>.\n    subtitle: Spatial normalization of the anatomical image\n    static: false\n\n- name: About\n  nested: true\n  reportlets:\n  - custom: errors\n    path: '{reportlets_dir}/{run_uuid}'\n    captions: <em>MRIQC</em> may have recorded failure conditions.\n    title: Errors\n  - metadata: \"input\"\n    settings:\n      # By default, only the first dictionary will be expanded.\n      # If folded is true, all will be folded. If false all expanded.\n      folded: true\n      # If an ID is not provided, one should be generated automatically\n      id: 'about-metadata'\n    caption: |\n      Thanks for using <em>MRIQC</em>. The following information may assist in\n      reconstructing the provenance of the corresponding derivatives.\n    title: Reproducibility and provenance information\n\n# Rating widget\nplugins:\n- module: nireports.assembler\n  path: data/rating-widget/bootstrap.yml\n"
  },
  {
    "path": "mriqc/data/config-example.toml",
    "content": "[environment]\ncpu_count = 8\nexec_env = \"posix\"\nfree_mem = 10.8\novercommit_policy = \"heuristic\"\novercommit_limit = \"50%\"\nnipype_version = \"1.4.2\"\ntemplateflow_version = \"0.5.2\"\nversion = \"0.15.2\"\n\n[execution]\nants_float = false\nbids_dir = \"data/\"\ndebug = false\ndry_run = false\ndsname = \"ds000005\"\nfloat32 = true\nlayout = \"BIDS Layout: data/ | Subjects: 16 | Sessions: 0 | Runs: 48\"\nlog_dir = \"derivatives/mriqc/logs\"\nlog_level = 15\nno_sub = false\noutput_dir = \"derivatives/\"\nparticipant_label = [ \"01\",]\nreports_only = false\nrun_uuid = \"20200403-185126_db5d5e64-4e98-4a75-b3d1-ab880afa0e85\"\ntemplateflow_home = \"/opt/templateflow\"\nupload_strict = false\nverbose_reports = false\nwebapi_url = \"https://mriqc.nimh.nih.gov/api/v1\"\nwebapi_port = 443\nwork_dir = \"work/\"\nwrite_graph = false\n\n[workflow]\nanalysis_level = [ \"participant\",]\nbiggest_file_gb = 0.03619009628891945\ndeoblique = false\ndespike = false\nfd_thres = 0.2\nfd_radius = 50\nfft_spikes_detector = false\nica = false\ntemplate_id = \"MNI152NLin2009cAsym\"\n\n[nipype]\ncrashfile_format = \"txt\"\nget_linked_libs = false\nnprocs = 8\nomp_nthreads = 8\nplugin = \"MultiProc\"\nresource_monitor = false\nstop_on_first_crash = true\n\n[workflow.inputs]\nbold = [ \"data/sub-01/func/sub-01_task-mixedgamblestask_run-01_bold.nii.gz\", \"data/sub-01/func/sub-01_task-mixedgamblestask_run-02_bold.nii.gz\", \"data/sub-01/func/sub-01_task-mixedgamblestask_run-03_bold.nii.gz\",]\nT1w = [ \"data/sub-01/anat/sub-01_T1w.nii.gz\",]\n\n[nipype.plugin_args]\nmaxtasksperchild = 1\nraise_insufficient = false\n"
  },
  {
    "path": "mriqc/data/config.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Utilities: Jinja2 templates.\"\"\"\n\nfrom pathlib import Path\n\nfrom niworkflows.data import Loader\n\n\nclass GroupTemplate:\n    \"\"\"Specific template for the individual report\"\"\"\n\n    \"\"\"\n    Utility class for generating a config file from a jinja template.\n    https://github.com/oesteban/endofday/blob/f2e79c625d648ef45b08cc1f11fd0bd84342d604/endofday/core/template.py\n    \"\"\"\n\n    def __init__(self):\n        import jinja2\n\n        self.template_str = Loader(__package__)('reports/group.html').absolute()\n        self.env = jinja2.Environment(\n            loader=jinja2.FileSystemLoader(searchpath='/'),\n            trim_blocks=True,\n            lstrip_blocks=True,\n            autoescape=False,  # noqa: S701\n        )\n\n    def compile(self, configs):\n        \"\"\"Generates a string with the replacements\"\"\"\n        template = self.env.get_template(str(self.template_str))\n        return template.render(configs)\n\n    def generate_conf(self, configs, path):\n        \"\"\"Saves the oucome after replacement on the template to file\"\"\"\n        Path(path).write_text(self.compile(configs))\n"
  },
  {
    "path": "mriqc/data/fsexport.tcl",
    "content": "# Sample script for setting up and taking screen shots. Scripting\n# reference is available at:\n\n# https://surfer.nmr.mgh.harvard.edu/fswiki/TkMeditGuide/TkMeditReference/TkMeditScripting\n\n# You can set the cursor or view center with the SetCursor command.\n\n\n\n# Alternatively you can set the slice number (in volume index\n# coordinates). This will not change the in-plane center.\n\n# Use SetZoomLevel to zoom in and out. 1 is normal, >1 is zoomed in,\n# and 0-1 is zoomed out.\n\n# SetZoomLevel level \nSetZoomLevel 2\n# SetZoomCenter 0 0 0\n\n# SetOrientation orientation \n# orientation:\n# 0     coronal\n# 1     horizontal\n# 2     sagittal\nSetOrientation 0\n\n# This command turns on and off various display flags.\n# SetDisplayFlag flag value \n# flag:\n# 1     Aux Volume - set to 1 to show aux volume\n# 2     Anatomical Volume - set to 0 to hide main and aux volume\n# 3     Cursor\n# 4     Main Surface\n# 5     Original Surface\n# 6     Pial Surface\n# 7     Interpolate Surface Vertices\n# 8     Surface Vertices\n# 9     Control Points\n# 10    Selection\n# 11    Functional Overlay\n# 12    Functional Color Scale Bar\n# 13    Mask to Functional Overlay\n# 14    Histogram Percent Change\n# 15    Segmentation Volume Overlay\n# 16    Aux Segmentation Volume\n# 17    Segmentation Label Volume Count\n# 18    DTI Overlay\n# 20    Focus Frame\n# 21    Undoable Voxels\n# 22    Axes\n# 23    Maximum Intensity Projection\n# 24    Head Points\n# 25    Verbose GCA DumpSetDisplayFlag \n\n# SetCursor coordinateSpace x y z \n# coordinateSpace:\n# 0     volume index\n# 1     RAS\n# 2     Talairach\nSetCursor 0 128 128 128\n\n# Turn cursor display off.\nSetDisplayFlag 3 0\n\n# Turn the axes on.\nSetDisplayFlag 22 1\n\n# Use this command to go to multiple views. This will copy the current\n# view settings from the current view, so all the above commands will\n# apply to all new views.\n# SetDisplayConfig numberOfColumns numberOfRows linkPolicy \n# linkPolicy:\n# 0     none\n# 1     linked cursors\n# 2     linked slice changes\n# SetDisplayConfig 2 2 1\n\n# Use the RedrawScreen command to force a redraw after you have a view\n# set up, before taking a picture.\n# RedrawScreen\n\n# This command will save the actual screenshot.\n# SaveTiff fileName\n# SaveTIFF $::env(FS_OUTPUT_PATH)/screenshot.tif\n\n\n# Use tcl loops to change orientations and take multiple\n# screenshots. This will set the view to a single view, and take three\n# screenshots, one of each orientation.\n# SetDisplayConfig 1 1 0\n# foreach orientation {0 1 2} label {cor horiz sag} {\n\n#     SetOrientation $orientation\n#     RedrawScreen\n#     SaveTIFF $::env(FS_OUTPUT_PATH)/screenshot-$label.tif\n# }\n\n# Or take pictures of multiple slices for a movie. This goes through\n# slices 0-255 and takes a shot at each one.\nfor { set slice 30 } { $slice < 226 } { incr slice } {\n    SetZoomCenter 160 80 $slice\n    SetSlice $slice\n    RedrawScreen\n    SaveTIFF $::env(FS_OUTPUT_PATH)[format \"/screenshot-%03d.tif\" $slice]\n}\n\nQuitMedit\n"
  },
  {
    "path": "mriqc/data/itk_identity.tfm",
    "content": "#Insight Transform File V1.0\n#Transform 0\nTransform: IdentityTransform_double_3_3\nParameters: \nFixedParameters: \n"
  },
  {
    "path": "mriqc/data/reports/embed_resources/boxplots.css",
    "content": "/* Hide data table */\n.csvdata {\n    display: none;\n}\n\nbody {\n    font-family: helvetica;\n}\n\n.text-warning {\n    font-weight: bold;\n    color: red;\n}\n/*Primary Chart*/\n\n/*Nested divs for responsiveness*/\n.chart-wrapper {\n    max-width: 800px;  /*Overwritten by the JS*/\n    min-width: 160px;\n    margin-bottom: 20px;\n    font-family: helvetica;\n}\n.chart-wrapper .inner-wrapper {\n    position: relative;\n    padding-bottom: 50%; /*Overwritten by the JS*/\n    width: 100%;\n}\n.chart-wrapper .outer-box {\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n}\n.chart-wrapper .inner-box {\n    width: 100%;\n    height: 100%;\n}\n\n.chart-wrapper text {\n  font-family: helvetica;\n  font-size: 13px;\n}\n\n.chart-wrapper .axis path,\n.chart-wrapper .axis line {\n    fill: none;\n    stroke: #888;\n    stroke-width: 2px;\n    shape-rendering: crispEdges;\n}\n\n.chart-wrapper .y.axis .tick line {\n    stroke: lightgrey;\n    opacity: 0.6;\n    stroke-dasharray: 2,1;\n    stroke-width: 1;\n    shape-rendering: crispEdges;\n\n}\n\n.chart-wrapper .x.axis .domain {\n  display: none;\n}\n\n.chart-wrapper div.tooltip {\n    position: absolute;\n    text-align: left;\n    padding: 3px;\n    font-size: 12px;\n    background: #eee;\n    border: 0px;\n    border-radius: 1px;\n    pointer-events: none;\n    opacity: .7;\n    z-index: 10;\n}\n\n/*Box Plot*/\n.chart-wrapper .box-plot .box {\n    fill-opacity: .5;\n    stroke-width: 2;\n}\n.chart-wrapper .box-plot line {\n    stroke-width: 2px;\n}\n.chart-wrapper .box-plot circle {\n    fill: white;\n    stroke: black;\n}\n\n.chart-wrapper .box-plot .median {\n    stroke: black;\n}\n\n.chart-wrapper .box-plot circle.median {\n    /*the script makes the circles the same color as the box, you can override this in the js*/\n    fill: white !important;\n}\n\n.chart-wrapper .box-plot .mean {\n    stroke: white;\n    stroke-dasharray: 2,1;\n    stroke-width: 1px;\n}\n\n@media (max-width:500px){\n    .chart-wrapper .box-plot circle {display: none;}\n}\n\n/*Violin Plot*/\n\n.chart-wrapper .violin-plot .area {\n    shape-rendering: geometricPrecision;\n    opacity: 0.4;\n}\n\n.chart-wrapper .violin-plot .line {\n    fill: none;\n    stroke-width: 2px;\n    shape-rendering: geometricPrecision;\n}\n\n/*Notch Plot*/\n.chart-wrapper .notch-plot .notch {\n    fill-opacity: 0.4;\n    stroke-width: 2;\n}\n\n/* Point Plots*/\n.chart-wrapper .points-plot .point {\n    /*stroke: black;\n    stroke-width: 1px;*/\n    fill-opacity: 0.4;\n}\n\n.chart-wrapper .metrics-lines {\n    stroke-width: 4px;\n}\n\n/* Non-Chart Styles for demo*/\n.chart-options  {\n    min-width: 200px;\n    font-size: 13px;\n    font-family: helvetica;\n}\n.chart-options button {\n    margin: 3px;\n    padding: 3px;\n    font-size: 12px;\n}\n.chart-options p {\n    display: inline;\n}\n@media (max-width:500px){\n    .chart-options p {display: block;}\n}"
  },
  {
    "path": "mriqc/data/reports/embed_resources/boxplots.js",
    "content": "/**\n * @fileOverview A D3 based distribution chart system. Supports: Box plots, Violin plots, Notched box plots, trend lines, beeswarm plot\n * @version 3.0\n */\n\n\n/**\n * Creates a box plot, violin plot, and or notched box plot\n * @param settings Configuration options for the base plot\n * @param settings.data The data for the plot\n * @param settings.xName The name of the column that should be used for the x groups\n * @param settings.yName The name of the column used for the y values\n * @param {string} settings.selector The selector string for the main chart div\n * @param [settings.axisLabels={}] Defaults to the xName and yName\n * @param [settings.yTicks = 1] 1 = default ticks. 2 =  double, 0.5 = half\n * @param [settings.scale='linear'] 'linear' or 'log' - y scale of the chart\n * @param [settings.chartSize={width:800, height:400}] The height and width of the chart itself (doesn't include the container)\n * @param [settings.margin={top: 15, right: 60, bottom: 40, left: 50}] The margins around the chart (inside the main div)\n * @param [settings.constrainExtremes=false] Should the y scale include outliers?\n * @returns {object} chart A chart object\n */\nfunction makeDistroChart(settings) {\n\n    var chart = {};\n\n    // Defaults\n    chart.settings = {\n        data: null,\n        xName: null,\n        yName: null,\n        axisLabels: {xAxis: null, yAxis: null},\n        labelName: \"label\",\n        unitsName: \"units\",\n        selector: null,\n        axisLables: null,\n        yTicks: 1,\n        scale: 'linear',\n        chartSize: {width: 800, height: 400},\n        margin: {top: 15, right: 10, bottom: 50, left: 50},\n        constrainExtremes: false,\n        color: d3.scale.category10(),\n        modality: null\n    };\n\n    for (var setting in settings) {\n        chart.settings[setting] = settings[setting]\n    }\n\n    function formatAsFloat(d) {\n        if (d % 1 !== 0) {\n            return d3.format(\".2f\")(d);\n        } else {\n            return d3.format(\".0f\")(d);\n        }\n    }\n\n    function logFormatNumber(d) {\n        var x = Math.log(d) / Math.log(10) + 1e-6;\n        return Math.abs(x - Math.floor(x)) < 0.6 ? formatAsFloat(d) : \"\";\n    }\n\n    chart.yFormatter = formatAsFloat;\n\n    chart.data = chart.settings.data;\n\n    iqmName = chart.data[0][chart.settings.xName]\n    if (iqmName.indexOf('_') > 0) {\n        iqmName = iqmName.substr(0, iqmName.indexOf('_'))\n    }\n    chart.settings.axisLabels.yAxis = iqmName.toUpperCase()\n    if (iqmName.toLowerCase().startsWith('fd') || iqmName.toLowerCase().startsWith('spikes')) {\n        chart.settings.constrainExtremes = true\n    }\n\n    units = chart.data[0][chart.settings.unitsName]\n    if (units) {\n        chart.settings.axisLabels.yAxis += ' (' + units + ')'\n    }\n\n\n    chart.groupObjs = {}; //The data organized by grouping and sorted as well as any metadata for the groups\n    chart.objs = {mainDiv: null, chartDiv: null, g: null, xAxis: null, yAxis: null};\n    chart.colorFunct = null;\n\n    /**\n     * Takes an array, function, or object mapping and created a color function from it\n     * @param {function|[]|object} colorOptions\n     * @returns {function} Function to be used to determine chart colors\n     */\n    function getColorFunct(colorOptions) {\n        if (typeof colorOptions == 'function') {\n            return colorOptions\n        } else if (Array.isArray(colorOptions)) {\n            //  If an array is provided, map it to the domain\n            var colorMap = {}, cColor = 0;\n            for (var cName in chart.groupObjs) {\n                colorMap[cName] = colorOptions[cColor];\n                cColor = (cColor + 1) % colorOptions.length;\n            }\n            return function (group) {\n                return colorMap[group];\n            }\n        } else if (typeof colorOptions == 'object') {\n            // if an object is provided, assume it maps to  the colors\n            return function (group) {\n                return colorOptions[group];\n            }\n        } else {\n            return d3.scale.category10();\n        }\n    }\n\n    /**\n     * Takes a percentage as returns the values that correspond to that percentage of the group range width\n     * @param objWidth Percentage of range band\n     * @param gName The bin name to use to get the x shift\n     * @returns {{left: null, right: null, middle: null}}\n     */\n    function getObjWidth(objWidth, gName) {\n        var objSize = {left: null, right: null, middle: null};\n        var width = chart.xScale.rangeBand() * (objWidth / 100);\n        var padding = (chart.xScale.rangeBand() - width) / 2;\n        var gShift = chart.xScale(gName);\n        objSize.middle = chart.xScale.rangeBand() / 2 + gShift;\n        objSize.left = padding + gShift;\n        objSize.right = objSize.left + width;\n        return objSize;\n    }\n\n    /**\n     * Adds jitter to the  scatter point plot\n     * @param doJitter true or false, add jitter to the point\n     * @param width percent of the range band to cover with the jitter\n     * @returns {number}\n     */\n    function addJitter(doJitter, width) {\n        if (doJitter !== true || width == 0) {\n            return 0\n        }\n        return Math.floor(Math.random() * width) - width / 2;\n    }\n\n    function shallowCopy(oldObj) {\n        var newObj = {};\n        for (var i in oldObj) {\n            if (oldObj.hasOwnProperty(i)) {\n                newObj[i] = oldObj[i];\n            }\n        }\n        return newObj;\n    }\n\n    /**\n     * Closure that creates the tooltip hover function\n     * @param groupName Name of the x group\n     * @param metrics Object to use to get values for the group\n     * @returns {Function} A function that provides the values for the tooltip\n     */\n    function tooltipHover(groupName, metrics) {\n        var tooltipString = \"Group: \" + groupName;\n        tooltipString += \"<br\\>Max: \" + formatAsFloat(metrics.max, 0.1);\n        tooltipString += \"<br\\>Q3: \" + formatAsFloat(metrics.quartile3);\n        tooltipString += \"<br\\>Median: \" + formatAsFloat(metrics.median);\n        tooltipString += \"<br\\>Q1: \" + formatAsFloat(metrics.quartile1);\n        tooltipString += \"<br\\>Min: \" + formatAsFloat(metrics.min);\n        return function () {\n            chart.objs.tooltip.transition().duration(200).style(\"opacity\", 0.9);\n            chart.objs.tooltip.html(tooltipString)\n        };\n    }\n\n    function axislabelHover(groupName) {\n        var tooltipString = \"Go to definition of \" + groupName;\n        return function () {\n            chart.objs.tooltip.transition().duration(200).style(\"opacity\", 1.0);\n            chart.objs.tooltip.html(tooltipString)\n        };\n    }\n\n    /**\n     * Closure that creates the tooltip hover function\n     * @param groupName Name of the x group\n     * @param metrics Object to use to get values for the group\n     * @returns {Function} A function that provides the values for the tooltip\n     */\n    function pointHover(label, value) {\n        var tooltipString = \"Subject: \" + label + \"<br\\>Measure: \" + value\n        return function () {\n            chart.objs.tooltip.transition().duration(200).style(\"opacity\", 1.0);\n            chart.objs.tooltip.html(tooltipString)\n        };\n    }\n\n    /**\n     * Parse the data and calculates base values for the plots\n     */\n    !function prepareData() {\n        function calcMetrics(values) {\n            // Do not reorder in-place\n            values = values.slice(0).sort(d3.ascending)\n\n            var metrics = { //These are the original non�scaled values\n                max: null,\n                upperOuterFence: null,\n                upperInnerFence: null,\n                quartile3: null,\n                median: null,\n                mean: null,\n                iqr: null,\n                quartile1: null,\n                lowerInnerFence: null,\n                lowerOuterFence: null,\n                min: null\n            };\n\n            metrics.min = d3.min(values);\n            metrics.quartile1 = d3.quantile(values, 0.25);\n            metrics.median = d3.median(values);\n            metrics.mean = d3.mean(values);\n            metrics.quartile3 = d3.quantile(values, 0.75);\n            metrics.max = d3.max(values);\n            metrics.iqr = metrics.quartile3 - metrics.quartile1;\n\n            //The inner fences are the closest value to the IQR without going past it (assumes sorted lists)\n            var LIF = metrics.quartile1 - (1.5 * metrics.iqr);\n            var UIF = metrics.quartile3 + (1.5 * metrics.iqr);\n            for (var i = 0; i <= values.length; i++) {\n                if (values[i] < LIF) {\n                    continue;\n                }\n                if (!metrics.lowerInnerFence && values[i] >= LIF) {\n                    metrics.lowerInnerFence = values[i];\n                    continue;\n                }\n                if (values[i] > UIF) {\n                    metrics.upperInnerFence = values[i - 1];\n                    break;\n                }\n            }\n\n\n            metrics.lowerOuterFence = metrics.quartile1 - (3 * metrics.iqr);\n            metrics.upperOuterFence = metrics.quartile3 + (3 * metrics.iqr);\n            if (!metrics.lowerInnerFence) {\n                metrics.lowerInnerFence = metrics.min;\n            }\n            if (!metrics.upperInnerFence) {\n                metrics.upperInnerFence = metrics.max;\n            }\n            return metrics\n        }\n\n        var current_x = null;\n        var current_y = null;\n        var current_row;\n\n        // Group the values\n        for (current_row = 0; current_row < chart.data.length; current_row++) {\n            current_x = chart.data[current_row][chart.settings.xName];\n            current_y = chart.data[current_row][chart.settings.yName];\n            current_label = chart.data[current_row][chart.settings.labelName];\n\n            if (chart.groupObjs.hasOwnProperty(current_x)) {\n                chart.groupObjs[current_x].values.push(current_y);\n                chart.groupObjs[current_x].labels.push(current_label);\n            } else {\n                chart.groupObjs[current_x] = {};\n                chart.groupObjs[current_x].values = [current_y];\n                chart.groupObjs[current_x].labels = [current_label];\n            }\n        }\n\n        for (var cName in chart.groupObjs) {\n            // chart.groupObjs[cName].values.sort(d3.ascending);\n            chart.groupObjs[cName].metrics = {};\n            chart.groupObjs[cName].metrics = calcMetrics(chart.groupObjs[cName].values);\n\n        }\n    }();\n\n    /**\n     * Prepare the chart settings and chart div and svg\n     */\n    !function prepareSettings() {\n        //Set base settings\n        chart.margin = chart.settings.margin;\n        chart.divWidth = chart.settings.chartSize.width;\n        chart.divHeight = chart.settings.chartSize.height;\n        chart.width = chart.divWidth - chart.margin.left - chart.margin.right;\n        chart.height = chart.divHeight - chart.margin.top - chart.margin.bottom;\n\n        if (chart.settings.axisLabels) {\n            chart.xAxisLable = chart.settings.axisLabels.xAxis;\n            chart.yAxisLable = chart.settings.axisLabels.yAxis;\n        }\n\n        if (chart.settings.scale === 'log') {\n            chart.yScale = d3.scale.log();\n            chart.yFormatter = logFormatNumber;\n        } else {\n            chart.yScale = d3.scale.linear();\n        }\n\n        if (chart.settings.constrainExtremes === true) {\n            var fences = [];\n            for (var cName in chart.groupObjs) {\n                fences.push(chart.groupObjs[cName].metrics.lowerInnerFence);\n                fences.push(chart.groupObjs[cName].metrics.upperInnerFence);\n            }\n            chart.range = d3.extent(fences);\n\n        } else {\n            chart.range = d3.extent(chart.data, function (d) {return d[chart.settings.yName];});\n        }\n\n        chart.colorFunct = getColorFunct(chart.settings.colors);\n\n        // Build Scale functions\n        chart.yScale.range([chart.height, 0]).domain(chart.range).nice().clamp(true);\n        chart.xScale = d3.scale.ordinal().domain(Object.keys(chart.groupObjs)).rangeBands([0, chart.width]);\n\n        //Build Axes Functions\n        chart.objs.yAxis = d3.svg.axis()\n            .scale(chart.yScale)\n            .orient(\"left\")\n            .tickFormat(chart.yFormatter)\n            .outerTickSize(0)\n            .innerTickSize(-chart.width + (chart.margin.right + chart.margin.left));\n        chart.objs.yAxis.ticks(chart.objs.yAxis.ticks()*chart.settings.yTicks);\n        chart.objs.xAxis = d3.svg.axis().scale(chart.xScale).orient(\"bottom\").tickSize(5);\n    }();\n\n    /**\n     * Updates the chart based on the current settings and window size\n     * @returns {*}\n     */\n    chart.update = function () {\n        // Update chart size based on view port size\n        chart.width = parseInt(chart.objs.chartDiv.style(\"width\"), 10) - (chart.margin.left + chart.margin.right);\n        chart.height = parseInt(chart.objs.chartDiv.style(\"height\"), 10) - (chart.margin.top + chart.margin.bottom);\n\n        // Update scale functions\n        chart.xScale.rangeBands([0, chart.width]);\n        chart.yScale.range([chart.height, 0]);\n\n        // Update the yDomain if the Violin plot clamp is set to -1 meaning it will extend the violins to make nice points\n        if (chart.violinPlots && chart.violinPlots.options.show == true && chart.violinPlots.options._yDomainVP != null) {\n            chart.yScale.domain(chart.violinPlots.options._yDomainVP).nice().clamp(true);\n        } else {\n            chart.yScale.domain(chart.range).nice().clamp(true);\n        }\n\n        //Update axes\n        chart.objs.g.select('.x.axis').attr(\"transform\", \"translate(0,\" + chart.height + \")\").call(chart.objs.xAxis)\n            .selectAll(\"text\")\n            .attr(\"y\", 5)\n            .attr(\"x\", -5)\n            .attr(\"transform\", \"rotate(-45)\")\n            .style(\"text-anchor\", \"end\");\n        chart.objs.g.select('.x.axis .label').attr(\"x\", chart.width / 2);\n        chart.objs.g.select('.y.axis').call(chart.objs.yAxis.innerTickSize(-chart.width));\n        chart.objs.g.select('.y.axis .label').attr(\"x\", -chart.height / 2);\n        chart.objs.chartDiv.select('svg').attr(\"width\", chart.width + (chart.margin.left + chart.margin.right)).attr(\"height\", chart.height + (chart.margin.top + chart.margin.bottom));\n\n        return chart;\n    };\n\n    /**\n     * Prepare the chart html elements\n     */\n    !function prepareChart() {\n        // Build main div and chart div\n        chart.objs.mainDiv = d3.select(chart.settings.selector)\n            .style(\"width\", chart.divWidth + \"px\")\n            .style(\"display\", \"inline-block\");\n        // Add all the divs to make it centered and responsive\n        chart.objs.mainDiv.append(\"div\")\n            .attr(\"class\", \"inner-wrapper\")\n            .style(\"padding-bottom\", (chart.divHeight / chart.divWidth) * 100 + \"%\")\n            .append(\"div\").attr(\"class\", \"outer-box\")\n            .append(\"div\").attr(\"class\", \"inner-box\");\n        // Capture the inner div for the chart (where the chart actually is)\n        chart.selector = chart.settings.selector + \" .inner-box\";\n        chart.objs.chartDiv = d3.select(chart.selector);\n        d3.select(window).on('resize.' + chart.selector, chart.update);\n\n        // Create the svg\n        chart.objs.g = chart.objs.chartDiv.append(\"svg\")\n            .attr(\"class\", \"chart-area\")\n            .attr(\"width\", chart.width + (chart.margin.left + chart.margin.right))\n            .attr(\"height\", chart.height + (chart.margin.top + chart.margin.bottom))\n            .append(\"g\")\n            .attr(\"transform\", \"translate(\" + chart.margin.left + \",\" + chart.margin.top + \")\");\n\n        // Create axes\n        chart.objs.axes = chart.objs.g.append(\"g\").attr(\"class\", \"axis\");\n        chart.objs.axes.append(\"g\")\n            .attr(\"class\", \"x axis\")\n            .attr(\"transform\", \"translate(0,\" + chart.height + \")\")\n            .call(chart.objs.xAxis);\n        chart.objs.axes.append(\"g\")\n            .attr(\"class\", \"y axis\")\n            .call(chart.objs.yAxis)\n            .append(\"text\")\n            //.attr(\"class\", \"label\")\n            .attr(\"transform\", \"rotate(-90)\")\n            //.attr(\"y\", -42)\n            .attr(\"y\", 6)\n            .attr(\"dy\", \".71em\")\n            //.attr(\"x\", -chart.height / 2)\n            .style(\"text-anchor\", \"end\")\n            .style(\"font-size\", \"16px\")\n            .append(\"a\")\n            .attr(\"xlink:href\", function(d) {\n                label = chart.yAxisLable.toLowerCase()\n                charidx = label.indexOf(' ')\n                if (charidx > 1) { label = label.substr(0, charidx); }\n                charidx = label.indexOf('_')\n                if (charidx > 1) { label = label.substr(0, charidx); }\n\n                return \"http://mriqc.readthedocs.io/en/latest/iqms/\" + chart.settings.modality.toLowerCase() + \".html#iqms-\" + label\n            })\n            .text(chart.yAxisLable)\n            .on(\"mouseover\", function () {\n                chart.objs.tooltip\n                    .style(\"display\", null)\n                    .style(\"left\", (d3.event.pageX) + \"px\")\n                    .style(\"top\", (d3.event.pageY - 28) + \"px\");\n            }).on(\"mouseout\", function () {\n                chart.objs.tooltip.style(\"display\", \"none\");\n            }).on(\"mousemove\", axislabelHover(chart.yAxisLable));\n\n        // Create tooltip div\n        chart.objs.tooltip = chart.objs.mainDiv.append('div').attr('class', 'tooltip');\n        for (var cName in chart.groupObjs) {\n            chart.groupObjs[cName].g = chart.objs.g.append(\"g\").attr(\"class\", \"group\");\n        }\n        chart.update();\n    }();\n\n    /**\n     * Render a violin plot on the current chart\n     * @param options\n     * @param [options.showViolinPlot=true] True or False, show the violin plot\n     * @param [options.resolution=100 default]\n     * @param [options.bandwidth=10 default] May need higher bandwidth for larger data sets\n     * @param [options.width=50] The max percent of the group rangeBand that the violin can be\n     * @param [options.interpolation=''] How to render the violin\n     * @param [options.clamp=0 default]\n     *   0 = keep data within chart min and max, clamp once data = 0. May extend beyond data set min and max\n     *   1 = clamp at min and max of data set. Possibly no tails\n     *  -1 = extend chart axis to make room for data to interpolate to 0. May extend axis and data set min and max\n     * @param [options.colors=chart default] The color mapping for the violin plot\n     * @returns {*} The chart object\n     */\n    chart.renderViolinPlot = function (options) {\n        chart.violinPlots = {};\n\n        var defaultOptions = {\n            show: true,\n            showViolinPlot: true,\n            resolution: 100,\n            bandwidth: 20,\n            width: 50,\n            interpolation: 'cardinal',\n            clamp: 1,\n            colors: chart.colorFunct,\n            _yDomainVP: null // If the Violin plot is set to close all violin plots, it may need to extend the domain, that extended domain is stored here\n        };\n        chart.violinPlots.options = shallowCopy(defaultOptions);\n        for (var option in options) {\n            chart.violinPlots.options[option] = options[option]\n        }\n        var vOpts = chart.violinPlots.options;\n\n        // Create violin plot objects\n        for (var cName in chart.groupObjs) {\n            chart.groupObjs[cName].violin = {};\n            chart.groupObjs[cName].violin.objs = {};\n        }\n\n        /**\n         * Take a new set of options and redraw the violin\n         * @param updateOptions\n         */\n        chart.violinPlots.change = function (updateOptions) {\n            if (updateOptions) {\n                for (var key in updateOptions) {\n                    vOpts[key] = updateOptions[key]\n                }\n            }\n\n            for (var cName in chart.groupObjs) {\n                chart.groupObjs[cName].violin.objs.g.remove()\n            }\n\n            chart.violinPlots.prepareViolin();\n            chart.violinPlots.update();\n        };\n\n        chart.violinPlots.reset = function () {\n            chart.violinPlots.change(defaultOptions)\n        };\n        chart.violinPlots.show = function (opts) {\n            if (opts !== undefined) {\n                opts.show = true;\n                if (opts.reset) {\n                    chart.violinPlots.reset()\n                }\n            } else {\n                opts = {show: true};\n            }\n            chart.violinPlots.change(opts);\n\n        };\n\n        chart.violinPlots.hide = function (opts) {\n            if (opts !== undefined) {\n                opts.show = false;\n                if (opts.reset) {\n                    chart.violinPlots.reset()\n                }\n            } else {\n                opts = {show: false};\n            }\n            chart.violinPlots.change(opts);\n\n        };\n\n        /**\n         * Update the violin obj values\n         */\n        chart.violinPlots.update = function () {\n            var cName, cViolinPlot;\n\n            for (cName in chart.groupObjs) {\n                cViolinPlot = chart.groupObjs[cName].violin;\n\n                // Build the violins sideways, so use the yScale for the xScale and make a new yScale\n                var xVScale = chart.yScale.copy();\n\n\n                // Create the Kernel Density Estimator Function\n                cViolinPlot.kde = kernelDensityEstimator(eKernel(vOpts.bandwidth), xVScale.ticks(vOpts.resolution));\n                cViolinPlot.kdedata = cViolinPlot.kde(chart.groupObjs[cName].values);\n\n                var interpolateMax = chart.groupObjs[cName].metrics.max,\n                    interpolateMin = chart.groupObjs[cName].metrics.min;\n\n                if (vOpts.clamp == 0 || vOpts.clamp == -1) { //\n                    // When clamp is 0, calculate the min and max that is needed to bring the violin plot to a point\n                    // interpolateMax = the Minimum value greater than the max where y = 0\n                    interpolateMax = d3.min(cViolinPlot.kdedata.filter(function (d) {\n                        return (d.x > chart.groupObjs[cName].metrics.max && d.y == 0)\n                    }), function (d) {\n                        return d.x;\n                    });\n                    // interpolateMin = the Maximum value less than the min where y = 0\n                    interpolateMin = d3.max(cViolinPlot.kdedata.filter(function (d) {\n                        return (d.x < chart.groupObjs[cName].metrics.min && d.y == 0)\n                    }), function (d) {\n                        return d.x;\n                    });\n                    // If clamp is -1 we need to extend the axes so that the violins come to a point\n                    if (vOpts.clamp == -1) {\n                        kdeTester = eKernelTest(eKernel(vOpts.bandwidth), chart.groupObjs[cName].values);\n                        if (!interpolateMax) {\n                            var interMaxY = kdeTester(chart.groupObjs[cName].metrics.max);\n                            var interMaxX = chart.groupObjs[cName].metrics.max;\n                            var count = 25; // Arbitrary limit to make sure we don't get an infinite loop\n                            while (count > 0 && interMaxY != 0) {\n                                interMaxY = kdeTester(interMaxX);\n                                interMaxX += 1;\n                                count -= 1;\n                            }\n                            interpolateMax = interMaxX;\n                        }\n                        if (!interpolateMin) {\n                            var interMinY = kdeTester(chart.groupObjs[cName].metrics.min);\n                            var interMinX = chart.groupObjs[cName].metrics.min;\n                            var count = 25;  // Arbitrary limit to make sure we don't get an infinite loop\n                            while (count > 0 && interMinY != 0) {\n                                interMinY = kdeTester(interMinX);\n                                interMinX -= 1;\n                                count -= 1;\n                            }\n                            interpolateMin = interMinX;\n                        }\n\n                    }\n                    // Check to see if the new values are outside the existing chart range\n                    //   If they are assign them to the master _yDomainVP\n                    if (!vOpts._yDomainVP) vOpts._yDomainVP = chart.range.slice(0);\n                    if (interpolateMin && interpolateMin < vOpts._yDomainVP[0]) {\n                        vOpts._yDomainVP[0] = interpolateMin;\n                    }\n                    if (interpolateMax && interpolateMax > vOpts._yDomainVP[1]) {\n                        vOpts._yDomainVP[1] = interpolateMax;\n                    }\n\n\n                }\n\n\n                if (vOpts.showViolinPlot) {\n                    chart.update();\n                    xVScale = chart.yScale.copy();\n\n                    // Need to recalculate the KDE because the xVScale changed\n                    cViolinPlot.kde = kernelDensityEstimator(eKernel(vOpts.bandwidth), xVScale.ticks(vOpts.resolution));\n                    cViolinPlot.kdedata = cViolinPlot.kde(chart.groupObjs[cName].values);\n                }\n\n                cViolinPlot.kdedata = cViolinPlot.kdedata\n                    .filter(function (d) {\n                        return (!interpolateMin || d.x >= interpolateMin)\n                    })\n                    .filter(function (d) {\n                        return (!interpolateMax || d.x <= interpolateMax)\n                    });\n            }\n            for (cName in chart.groupObjs) {\n                cViolinPlot = chart.groupObjs[cName].violin;\n\n                // Get the violin width\n                var objBounds = getObjWidth(vOpts.width, cName);\n                var width = (objBounds.right - objBounds.left) / 2;\n\n                var yVScale = d3.scale.linear()\n                    .range([width, 0])\n                    .domain([0, d3.max(cViolinPlot.kdedata, function (d) {return d.y;})])\n                    .clamp(true);\n\n                var area = d3.svg.area()\n                    .interpolate(vOpts.interpolation)\n                    .x(function (d) {return xVScale(d.x);})\n                    .y0(width)\n                    .y1(function (d) {return yVScale(d.y);});\n\n                var line = d3.svg.line()\n                    .interpolate(vOpts.interpolation)\n                    .x(function (d) {return xVScale(d.x);})\n                    .y(function (d) {return yVScale(d.y)});\n\n                if (cViolinPlot.objs.left.area) {\n                    cViolinPlot.objs.left.area\n                        .datum(cViolinPlot.kdedata)\n                        .attr(\"d\", area);\n                    cViolinPlot.objs.left.line\n                        .datum(cViolinPlot.kdedata)\n                        .attr(\"d\", line);\n\n                    cViolinPlot.objs.right.area\n                        .datum(cViolinPlot.kdedata)\n                        .attr(\"d\", area);\n                    cViolinPlot.objs.right.line\n                        .datum(cViolinPlot.kdedata)\n                        .attr(\"d\", line);\n                }\n\n                // Rotate the violins\n                cViolinPlot.objs.left.g.attr(\"transform\", \"rotate(90,0,0)   translate(0,-\" + objBounds.left + \")  scale(1,-1)\");\n                cViolinPlot.objs.right.g.attr(\"transform\", \"rotate(90,0,0)  translate(0,-\" + objBounds.right + \")\");\n            }\n        };\n\n        /**\n         * Create the svg elements for the violin plot\n         */\n        chart.violinPlots.prepareViolin = function () {\n            var cName, cViolinPlot;\n\n            if (vOpts.colors) {\n                chart.violinPlots.color = getColorFunct(vOpts.colors);\n            } else {\n                chart.violinPlots.color = chart.colorFunct\n            }\n\n            if (vOpts.show == false) {return}\n\n            for (cName in chart.groupObjs) {\n                cViolinPlot = chart.groupObjs[cName].violin;\n\n                cViolinPlot.objs.g = chart.groupObjs[cName].g.append(\"g\").attr(\"class\", \"violin-plot\");\n                cViolinPlot.objs.left = {area: null, line: null, g: null};\n                cViolinPlot.objs.right = {area: null, line: null, g: null};\n\n                cViolinPlot.objs.left.g = cViolinPlot.objs.g.append(\"g\");\n                cViolinPlot.objs.right.g = cViolinPlot.objs.g.append(\"g\");\n\n                if (vOpts.showViolinPlot !== false) {\n                    //Area\n                    cViolinPlot.objs.left.area = cViolinPlot.objs.left.g.append(\"path\")\n                        .attr(\"class\", \"area\")\n                        .style(\"fill\", chart.violinPlots.color(cName));\n                    cViolinPlot.objs.right.area = cViolinPlot.objs.right.g.append(\"path\")\n                        .attr(\"class\", \"area\")\n                        .style(\"fill\", chart.violinPlots.color(cName));\n\n                    //Lines\n                    cViolinPlot.objs.left.line = cViolinPlot.objs.left.g.append(\"path\")\n                        .attr(\"class\", \"line\")\n                        .attr(\"fill\", 'none')\n                        .style(\"stroke\", chart.violinPlots.color(cName));\n                    cViolinPlot.objs.right.line = cViolinPlot.objs.right.g.append(\"path\")\n                        .attr(\"class\", \"line\")\n                        .attr(\"fill\", 'none')\n                        .style(\"stroke\", chart.violinPlots.color(cName));\n                }\n\n            }\n\n        };\n\n\n        function kernelDensityEstimator(kernel, x) {\n            return function (sample) {\n                return x.map(function (x) {\n                    return {x:x, y:d3.mean(sample, function (v) {return kernel(x - v);})};\n                });\n            };\n        }\n\n        function eKernel(scale) {\n            return function (u) {\n                return Math.abs(u /= scale) <= 1 ? .75 * (1 - u * u) / scale : 0;\n            };\n        }\n\n        // Used to find the roots for adjusting violin axis\n        // Given an array, find the value for a single point, even if it is not in the domain\n        function eKernelTest(kernel, array) {\n            return function (testX) {\n                return d3.mean(array, function (v) {return kernel(testX - v);})\n            }\n        }\n\n        chart.violinPlots.prepareViolin();\n\n        d3.select(window).on('resize.' + chart.selector + '.violinPlot', chart.violinPlots.update);\n        chart.violinPlots.update();\n        return chart;\n    };\n\n    /**\n     * Render a box plot on the current chart\n     * @param options\n     * @param [options.show=true] Toggle the whole plot on and off\n     * @param [options.showBox=true] Show the box part of the box plot\n     * @param [options.showWhiskers=true] Show the whiskers\n     * @param [options.showMedian=true] Show the median line\n     * @param [options.showMean=false] Show the mean line\n     * @param [options.medianCSize=3] The size of the circle on the median\n     * @param [options.showOutliers=true] Plot outliers\n     * @param [options.boxwidth=30] The max percent of the group rangeBand that the box can be\n     * @param [options.lineWidth=boxWidth] The max percent of the group rangeBand that the line can be\n     * @param [options.outlierScatter=false] Spread out the outliers so they don't all overlap (in development)\n     * @param [options.outlierCSize=2] Size of the outliers\n     * @param [options.colors=chart default] The color mapping for the box plot\n     * @returns {*} The chart object\n     */\n    chart.renderBoxPlot = function (options) {\n        chart.boxPlots = {};\n\n        // Defaults\n        var defaultOptions = {\n            show: true,\n            showBox: true,\n            showWhiskers: true,\n            showMedian: true,\n            showMean: false,\n            medianCSize: 3.5,\n            showOutliers: true,\n            boxWidth: 30,\n            lineWidth: null,\n            scatterOutliers: false,\n            outlierCSize: 2.5,\n            colors: chart.colorFunct,\n            padding: 0\n        };\n        chart.boxPlots.options = shallowCopy(defaultOptions);\n        for (var option in options) {\n            chart.boxPlots.options[option] = options[option]\n        }\n        var bOpts = chart.boxPlots.options;\n\n        //Create box plot objects\n        for (var cName in chart.groupObjs) {\n            chart.groupObjs[cName].boxPlot = {};\n            chart.groupObjs[cName].boxPlot.objs = {};\n        }\n\n\n        /**\n         * Calculates all the outlier points for each group\n         */\n        !function calcAllOutliers() {\n\n            /**\n             * Create lists of the outliers for each content group\n             * @param cGroup The object to modify\n             * @return null Modifies the object in place\n             */\n            function calcOutliers(cGroup) {\n                var cExtremes = [];\n                var cOutliers = [];\n                var cOut, idx;\n                for (idx = 0; idx <= cGroup.values.length; idx++) {\n                    cOut = {value: cGroup.values[idx]};\n\n                    if (cOut.value < cGroup.metrics.lowerInnerFence) {\n                        if (cOut.value < cGroup.metrics.lowerOuterFence) {\n                            cExtremes.push(cOut);\n                        } else {\n                            cOutliers.push(cOut);\n                        }\n                    } else if (cOut.value > cGroup.metrics.upperInnerFence) {\n                        if (cOut.value > cGroup.metrics.upperOuterFence) {\n                            cExtremes.push(cOut);\n                        } else {\n                            cOutliers.push(cOut);\n                        }\n                    }\n                }\n                cGroup.boxPlot.objs.outliers = cOutliers;\n                cGroup.boxPlot.objs.extremes = cExtremes;\n            }\n\n            for (var cName in chart.groupObjs) {\n                calcOutliers(chart.groupObjs[cName]);\n            }\n        }();\n\n        /**\n         * Take updated options and redraw the box plot\n         * @param updateOptions\n         */\n        chart.boxPlots.change = function (updateOptions) {\n            if (updateOptions) {\n                for (var key in updateOptions) {\n                    bOpts[key] = updateOptions[key]\n                }\n            }\n\n            for (var cName in chart.groupObjs) {\n                chart.groupObjs[cName].boxPlot.objs.g.remove()\n            }\n            chart.boxPlots.prepareBoxPlot();\n            chart.boxPlots.update()\n        };\n\n        chart.boxPlots.reset = function () {\n            chart.boxPlots.change(defaultOptions)\n        };\n        chart.boxPlots.show = function (opts) {\n            if (opts !== undefined) {\n                opts.show = true;\n                if (opts.reset) {\n                    chart.boxPlots.reset()\n                }\n            } else {\n                opts = {show: true};\n            }\n            chart.boxPlots.change(opts)\n\n        };\n        chart.boxPlots.hide = function (opts) {\n            if (opts !== undefined) {\n                opts.show = false;\n                if (opts.reset) {\n                    chart.boxPlots.reset()\n                }\n            } else {\n                opts = {show: false};\n            }\n            chart.boxPlots.change(opts)\n        };\n\n        /**\n         * Update the box plot obj values\n         */\n        chart.boxPlots.update = function () {\n            var cName, cBoxPlot;\n\n            for (cName in chart.groupObjs) {\n                cBoxPlot = chart.groupObjs[cName].boxPlot;\n\n                // Get the box width\n                var objBounds = getObjWidth(bOpts.boxWidth, cName);\n                objBounds.middle += chart.boxPlots.options.padding\n                objBounds.right += chart.boxPlots.options.padding\n                objBounds.left += chart.boxPlots.options.padding\n                var width = (objBounds.right - objBounds.left);\n\n                var sMetrics = {}; //temp var for scaled (plottable) metric values\n                for (var attr in chart.groupObjs[cName].metrics) {\n                    sMetrics[attr] = null;\n                    sMetrics[attr] = chart.yScale(chart.groupObjs[cName].metrics[attr]);\n                }\n\n                // Box\n                if (cBoxPlot.objs.box) {\n                    cBoxPlot.objs.box\n                        .attr(\"x\", objBounds.left)\n                        .attr('width', width)\n                        .attr(\"y\", sMetrics.quartile3)\n                        .attr(\"rx\", 1)\n                        .attr(\"ry\", 1)\n                        .attr(\"height\", -sMetrics.quartile3 + sMetrics.quartile1)\n                }\n\n                // Lines\n                var lineBounds = null;\n                if (bOpts.lineWidth) {\n                    lineBounds = getObjWidth(bOpts.lineWidth, cName)\n                } else {\n                    lineBounds = objBounds\n                }\n\n                // Apply padding\n                lineBounds.middle += chart.boxPlots.options.padding\n                lineBounds.right += chart.boxPlots.options.padding\n                lineBounds.left += chart.boxPlots.options.padding\n\n                // --Whiskers\n                if (cBoxPlot.objs.upperWhisker) {\n                    cBoxPlot.objs.upperWhisker.fence\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', sMetrics.upperInnerFence)\n                        .attr(\"y2\", sMetrics.upperInnerFence);\n                    cBoxPlot.objs.upperWhisker.line\n                        .attr(\"x1\", lineBounds.middle)\n                        .attr(\"x2\", lineBounds.middle)\n                        .attr('y1', sMetrics.quartile3)\n                        .attr(\"y2\", sMetrics.upperInnerFence);\n\n                    cBoxPlot.objs.lowerWhisker.fence\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', sMetrics.lowerInnerFence)\n                        .attr(\"y2\", sMetrics.lowerInnerFence);\n                    cBoxPlot.objs.lowerWhisker.line\n                        .attr(\"x1\", lineBounds.middle)\n                        .attr(\"x2\", lineBounds.middle)\n                        .attr('y1', sMetrics.quartile1)\n                        .attr(\"y2\", sMetrics.lowerInnerFence);\n                }\n\n                // --Median\n                if (cBoxPlot.objs.median) {\n                    cBoxPlot.objs.median.line\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', sMetrics.median)\n                        .attr(\"y2\", sMetrics.median);\n                    cBoxPlot.objs.median.circle\n                        .attr(\"cx\", lineBounds.middle)\n                        .attr(\"cy\", sMetrics.median)\n                }\n\n                // --Mean\n                if (cBoxPlot.objs.mean) {\n                    cBoxPlot.objs.mean.line\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', sMetrics.mean)\n                        .attr(\"y2\", sMetrics.mean);\n                    cBoxPlot.objs.mean.circle\n                        .attr(\"cx\", lineBounds.middle)\n                        .attr(\"cy\", sMetrics.mean);\n                }\n\n                // Outliers\n\n                var pt;\n                if (cBoxPlot.objs.outliers) {\n                    for (pt in cBoxPlot.objs.outliers) {\n                        cBoxPlot.objs.outliers[pt].point\n                            .attr(\"cx\", objBounds.middle + addJitter(bOpts.scatterOutliers, width))\n                            .attr(\"cy\", chart.yScale(cBoxPlot.objs.outliers[pt].value));\n                    }\n                }\n                if (cBoxPlot.objs.extremes) {\n                    for (pt in cBoxPlot.objs.extremes) {\n                        cBoxPlot.objs.extremes[pt].point\n                            .attr(\"cx\", objBounds.middle + addJitter(bOpts.scatterOutliers, width))\n                            .attr(\"cy\", chart.yScale(cBoxPlot.objs.extremes[pt].value));\n                    }\n                }\n            }\n        };\n\n        /**\n         * Create the svg elements for the box plot\n         */\n        chart.boxPlots.prepareBoxPlot = function () {\n            var cName, cBoxPlot;\n\n            if (bOpts.colors) {\n                chart.boxPlots.colorFunct = getColorFunct(bOpts.colors);\n            } else {\n                chart.boxPlots.colorFunct = chart.colorFunct\n            }\n\n            if (bOpts.show == false) {\n                return\n            }\n\n            for (cName in chart.groupObjs) {\n                cBoxPlot = chart.groupObjs[cName].boxPlot;\n\n                cBoxPlot.objs.g = chart.groupObjs[cName].g.append(\"g\").attr(\"class\", \"box-plot\");\n\n                //Plot Box (default show)\n                if (bOpts.showBox) {\n                    cBoxPlot.objs.box = cBoxPlot.objs.g.append(\"rect\")\n                        .attr(\"class\", \"box\")\n                        .style(\"fill\", chart.boxPlots.colorFunct(cName))\n                        .style(\"stroke\", chart.boxPlots.colorFunct(cName))\n                        .on(\"mouseover\", function () {\n                            chart.objs.tooltip\n                                .style(\"display\", null)\n                                .style(\"left\", (d3.event.pageX) + \"px\")\n                                .style(\"top\", (d3.event.pageY - 28) + \"px\");\n                        }).on(\"mouseout\", function () {\n                            chart.objs.tooltip.style(\"display\", \"none\");\n                        }).on(\"mousemove\", tooltipHover(cName, chart.groupObjs[cName].metrics));\n                    //A stroke is added to the box with the group color, it is\n                    // hidden by default and can be shown through css with stroke-width\n                }\n\n                //Plot Median (default show)\n                if (bOpts.showMedian) {\n                    cBoxPlot.objs.median = {line: null, circle: null};\n                    cBoxPlot.objs.median.line = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"median\");\n                    cBoxPlot.objs.median.circle = cBoxPlot.objs.g.append(\"circle\")\n                        .attr(\"class\", \"median\")\n                        .attr('r', bOpts.medianCSize)\n                        .style(\"fill\", chart.boxPlots.colorFunct(cName));\n                }\n\n                // Plot Mean (default no plot)\n                if (bOpts.showMean) {\n                    cBoxPlot.objs.mean = {line: null, circle: null};\n                    cBoxPlot.objs.mean.line = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"mean\");\n                    cBoxPlot.objs.mean.circle = cBoxPlot.objs.g.append(\"circle\")\n                        .attr(\"class\", \"mean\")\n                        .attr('r', bOpts.medianCSize)\n                        .style(\"fill\", chart.boxPlots.colorFunct(cName));\n                }\n\n                // Plot Whiskers (default show)\n                if (bOpts.showWhiskers) {\n                    cBoxPlot.objs.upperWhisker = {fence: null, line: null};\n                    cBoxPlot.objs.lowerWhisker = {fence: null, line: null};\n                    cBoxPlot.objs.upperWhisker.fence = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"upper whisker\")\n                        .style(\"stroke\", chart.boxPlots.colorFunct(cName));\n                    cBoxPlot.objs.upperWhisker.line = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"upper whisker\")\n                        .style(\"stroke\", chart.boxPlots.colorFunct(cName));\n\n                    cBoxPlot.objs.lowerWhisker.fence = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"lower whisker\")\n                        .style(\"stroke\", chart.boxPlots.colorFunct(cName));\n                    cBoxPlot.objs.lowerWhisker.line = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"lower whisker\")\n                        .style(\"stroke\", chart.boxPlots.colorFunct(cName));\n                }\n\n                // Plot outliers (default show)\n                if (bOpts.showOutliers) {\n                    if (!cBoxPlot.objs.outliers) calcAllOutliers();\n                    var pt;\n                    if (cBoxPlot.objs.outliers.length) {\n                        var outDiv = cBoxPlot.objs.g.append(\"g\").attr(\"class\", \"boxplot outliers\");\n                        for (pt in cBoxPlot.objs.outliers) {\n                            cBoxPlot.objs.outliers[pt].point = outDiv.append(\"circle\")\n                                .attr(\"class\", \"outlier\")\n                                .attr('r', bOpts.outlierCSize)\n                                .style(\"fill\", chart.boxPlots.colorFunct(cName));\n                        }\n                    }\n\n                    if (cBoxPlot.objs.extremes.length) {\n                        var extDiv = cBoxPlot.objs.g.append(\"g\").attr(\"class\", \"boxplot extremes\");\n                        for (pt in cBoxPlot.objs.extremes) {\n                            cBoxPlot.objs.extremes[pt].point = extDiv.append(\"circle\")\n                                .attr(\"class\", \"extreme\")\n                                .attr('r', bOpts.outlierCSize)\n                                .style(\"stroke\", chart.boxPlots.colorFunct(cName));\n                        }\n                    }\n                }\n\n\n            }\n        };\n        chart.boxPlots.prepareBoxPlot();\n\n        d3.select(window).on('resize.' + chart.selector + '.boxPlot', chart.boxPlots.update);\n        chart.boxPlots.update();\n        return chart;\n\n    };\n\n    /**\n     * Render a notched box on the current chart\n     * @param options\n     * @param [options.show=true] Toggle the whole plot on and off\n     * @param [options.showNotchBox=true] Show the notch box\n     * @param [options.showLines=false] Show lines at the confidence intervals\n     * @param [options.boxWidth=35] The width of the widest part of the box\n     * @param [options.medianWidth=20] The width of the narrowist part of the box\n     * @param [options.lineWidth=50] The width of the confidence interval lines\n     * @param [options.notchStyle=null] null=traditional style, 'box' cuts out the whole notch in right angles\n     * @param [options.colors=chart default] The color mapping for the notch boxes\n     * @returns {*} The chart object\n     */\n    chart.renderNotchBoxes = function (options) {\n        chart.notchBoxes = {};\n\n        //Defaults\n        var defaultOptions = {\n            show: true,\n            showNotchBox: true,\n            showLines: false,\n            boxWidth: 35,\n            medianWidth: 20,\n            lineWidth: 50,\n            notchStyle: null,\n            colors: null\n        };\n        chart.notchBoxes.options = shallowCopy(defaultOptions);\n        for (var option in options) {\n            chart.notchBoxes.options[option] = options[option]\n        }\n        var nOpts = chart.notchBoxes.options;\n\n        //Create notch objects\n        for (var cName in chart.groupObjs) {\n            chart.groupObjs[cName].notchBox = {};\n            chart.groupObjs[cName].notchBox.objs = {};\n        }\n\n        /**\n         * Makes the svg path string for a notched box\n         * @param cNotch Current notch box object\n         * @param notchBounds objBound object\n         * @returns {string} A string in the proper format for a svg polygon\n         */\n        function makeNotchBox(cNotch, notchBounds) {\n            var scaledValues = [];\n            if (nOpts.notchStyle == 'box') {\n                scaledValues = [\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile1)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.medianLeft, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.medianLeft, chart.yScale(cNotch.metrics.median)],\n                    [notchBounds.medianLeft, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile3)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile3)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.medianRight, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.medianRight, chart.yScale(cNotch.metrics.median)],\n                    [notchBounds.medianRight, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile1)]\n                ];\n            } else {\n                scaledValues = [\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile1)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.medianLeft, chart.yScale(cNotch.metrics.median)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile3)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile3)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.medianRight, chart.yScale(cNotch.metrics.median)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile1)]\n                ];\n            }\n            return scaledValues.map(function (d) {\n                return [d[0], d[1]].join(\",\");\n            }).join(\" \");\n        }\n\n        /**\n         * Calculate the confidence intervals\n         */\n        !function calcNotches() {\n            var cNotch, modifier;\n            for (var cName in chart.groupObjs) {\n                cNotch = chart.groupObjs[cName];\n                modifier = (1.57 * (cNotch.metrics.iqr / Math.sqrt(cNotch.values.length)));\n                cNotch.metrics.upperNotch = cNotch.metrics.median + modifier;\n                cNotch.metrics.lowerNotch = cNotch.metrics.median - modifier;\n            }\n        }();\n\n        /**\n         * Take a new set of options and redraw the notch boxes\n         * @param updateOptions\n         */\n        chart.notchBoxes.change = function (updateOptions) {\n            if (updateOptions) {\n                for (var key in updateOptions) {\n                    nOpts[key] = updateOptions[key]\n                }\n            }\n\n            for (var cName in chart.groupObjs) {\n                chart.groupObjs[cName].notchBox.objs.g.remove()\n            }\n            chart.notchBoxes.prepareNotchBoxes();\n            chart.notchBoxes.update();\n        };\n\n        chart.notchBoxes.reset = function () {\n            chart.notchBoxes.change(defaultOptions)\n        };\n        chart.notchBoxes.show = function (opts) {\n            if (opts !== undefined) {\n                opts.show = true;\n                if (opts.reset) {\n                    chart.notchBoxes.reset()\n                }\n            } else {\n                opts = {show: true};\n            }\n            chart.notchBoxes.change(opts)\n        };\n        chart.notchBoxes.hide = function (opts) {\n            if (opts !== undefined) {\n                opts.show = false;\n                if (opts.reset) {\n                    chart.notchBoxes.reset()\n                }\n            } else {\n                opts = {show: false};\n            }\n            chart.notchBoxes.change(opts)\n        };\n\n        /**\n         * Update the notch box obj values\n         */\n        chart.notchBoxes.update = function () {\n            var cName, cGroup;\n\n            for (cName in chart.groupObjs) {\n                cGroup = chart.groupObjs[cName];\n\n                // Get the box size\n                var boxBounds = getObjWidth(nOpts.boxWidth, cName);\n                var medianBounds = getObjWidth(nOpts.medianWidth, cName);\n\n                var notchBounds = {\n                    boxLeft: boxBounds.left,\n                    boxRight: boxBounds.right,\n                    middle: boxBounds.middle,\n                    medianLeft: medianBounds.left,\n                    medianRight: medianBounds.right\n                };\n\n                // Notch Box\n                if (cGroup.notchBox.objs.notch) {\n                    cGroup.notchBox.objs.notch\n                        .attr(\"points\", makeNotchBox(cGroup, notchBounds));\n                }\n                if (cGroup.notchBox.objs.upperLine) {\n                    var lineBounds = null;\n                    if (nOpts.lineWidth) {\n                        lineBounds = getObjWidth(nOpts.lineWidth, cName)\n                    } else {\n                        lineBounds = objBounds\n                    }\n\n                    var confidenceLines = {\n                        upper: chart.yScale(cGroup.metrics.upperNotch),\n                        lower: chart.yScale(cGroup.metrics.lowerNotch)\n                    };\n                    cGroup.notchBox.objs.upperLine\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', confidenceLines.upper)\n                        .attr(\"y2\", confidenceLines.upper);\n                    cGroup.notchBox.objs.lowerLine\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', confidenceLines.lower)\n                        .attr(\"y2\", confidenceLines.lower);\n                }\n            }\n        };\n\n        /**\n         * Create the svg elements for the notch boxes\n         */\n        chart.notchBoxes.prepareNotchBoxes = function () {\n            var cName, cNotch;\n\n            if (nOpts && nOpts.colors) {\n                chart.notchBoxes.colorFunct = getColorFunct(nOpts.colors);\n            } else {\n                chart.notchBoxes.colorFunct = chart.colorFunct\n            }\n\n            if (nOpts.show == false) {\n                return\n            }\n\n            for (cName in chart.groupObjs) {\n                cNotch = chart.groupObjs[cName].notchBox;\n\n                cNotch.objs.g = chart.groupObjs[cName].g.append(\"g\").attr(\"class\", \"notch-plot\");\n\n                // Plot Box (default show)\n                if (nOpts.showNotchBox) {\n                    cNotch.objs.notch = cNotch.objs.g.append(\"polygon\")\n                        .attr(\"class\", \"notch\")\n                        .style(\"fill\", chart.notchBoxes.colorFunct(cName))\n                        .style(\"stroke\", chart.notchBoxes.colorFunct(cName));\n                    //A stroke is added to the notch with the group color, it is\n                    // hidden by default and can be shown through css with stroke-width\n                }\n\n                //Plot Confidence Lines (default hide)\n                if (nOpts.showLines) {\n                    cNotch.objs.upperLine = cNotch.objs.g.append(\"line\")\n                        .attr(\"class\", \"upper confidence line\")\n                        .style(\"stroke\", chart.notchBoxes.colorFunct(cName));\n\n                    cNotch.objs.lowerLine = cNotch.objs.g.append(\"line\")\n                        .attr(\"class\", \"lower confidence line\")\n                        .style(\"stroke\", chart.notchBoxes.colorFunct(cName));\n                }\n            }\n        };\n        chart.notchBoxes.prepareNotchBoxes();\n\n        d3.select(window).on('resize.' + chart.selector + '.notchBox', chart.notchBoxes.update);\n        chart.notchBoxes.update();\n        return chart;\n    };\n\n    /**\n     * Render a raw data in various forms\n     * @param options\n     * @param [options.show=true] Toggle the whole plot on and off\n     * @param [options.showPlot=false] True or false, show points\n     * @param [options.plotType='none'] Options: no scatter = (false or 'none'); scatter points= (true or [amount=% of width (default=10)]); beeswarm points = ('beeswarm')\n     * @param [options.pointSize=6] Diameter of the circle in pizels (not the radius)\n     * @param [options.showLines=['median']] Can equal any of the metrics lines\n     * @param [options.showbeanLines=false] Options: no lines = false\n     * @param [options.beanWidth=20] % width\n     * @param [options.colors=chart default]\n     * @returns {*} The chart object\n     *\n     */\n    chart.renderDataPlots = function (options) {\n        chart.dataPlots = {};\n\n\n        //Defaults\n        var defaultOptions = {\n            show: true,\n            showPlot: false,\n            plotType: 'none',\n            pointSize: 6,\n            showLines: false,//['median'],\n            showBeanLines: false,\n            beanWidth: 20,\n            colors: null,\n            padding: 0\n        };\n        chart.dataPlots.options = shallowCopy(defaultOptions);\n        for (var option in options) {\n            chart.dataPlots.options[option] = options[option]\n        }\n        var dOpts = chart.dataPlots.options;\n\n        //Create notch objects\n        for (var cName in chart.groupObjs) {\n            chart.groupObjs[cName].dataPlots = {};\n            chart.groupObjs[cName].dataPlots.objs = {};\n        }\n        // The lines don't fit into a group bucket so they live under the dataPlot object\n        chart.dataPlots.objs = {};\n\n        /**\n         * Take updated options and redraw the data plots\n         * @param updateOptions\n         */\n        chart.dataPlots.change = function (updateOptions) {\n            if (updateOptions) {\n                for (var key in updateOptions) {\n                    dOpts[key] = updateOptions[key]\n                }\n            }\n\n            chart.dataPlots.objs.g.remove();\n            for (var cName in chart.groupObjs) {\n                chart.groupObjs[cName].dataPlots.objs.g.remove()\n            }\n            chart.dataPlots.preparePlots();\n            chart.dataPlots.update()\n        };\n\n        chart.dataPlots.reset = function () {\n            chart.dataPlots.change(defaultOptions)\n        };\n        chart.dataPlots.show = function (opts) {\n            if (opts !== undefined) {\n                opts.show = true;\n                if (opts.reset) {\n                    chart.dataPlots.reset()\n                }\n            } else {\n                opts = {show: true};\n            }\n            chart.dataPlots.change(opts)\n        };\n        chart.dataPlots.hide = function (opts) {\n            if (opts !== undefined) {\n                opts.show = false;\n                if (opts.reset) {\n                    chart.dataPlots.reset()\n                }\n            } else {\n                opts = {show: false};\n            }\n            chart.dataPlots.change(opts)\n        };\n\n        /**\n         * Update the data plot obj values\n         */\n        chart.dataPlots.update = function () {\n            var cName, cGroup, cPlot;\n\n            // Metrics lines\n            if (chart.dataPlots.objs.g) {\n                var halfBand = chart.xScale.rangeBand() / 2; // find the middle of each band\n                for (var cMetric in chart.dataPlots.objs.lines) {\n                    chart.dataPlots.objs.lines[cMetric].line\n                        .x(function (d) {\n                            return chart.xScale(d.x) + halfBand\n                        });\n                    chart.dataPlots.objs.lines[cMetric].g\n                        .datum(chart.dataPlots.objs.lines[cMetric].values)\n                        .attr('d', chart.dataPlots.objs.lines[cMetric].line);\n                }\n            }\n\n\n            for (cName in chart.groupObjs) {\n                cGroup = chart.groupObjs[cName];\n                cPlot = cGroup.dataPlots;\n\n                if (cPlot.objs.points) {\n                    if (dOpts.plotType == 'beeswarm') {\n                        var swarmBounds = getObjWidth(100, cName);\n                        var yPtScale = chart.yScale.copy()\n                            .range([Math.floor(chart.yScale.range()[0] / dOpts.pointSize), 0])\n                            .interpolate(d3.interpolateRound)\n                            .domain(chart.yScale.domain());\n                        var maxWidth = Math.floor(chart.xScale.rangeBand() / dOpts.pointSize);\n                        var ptsObj = {};\n                        var cYBucket = null;\n                        //  Bucket points\n                        for (var pt = 0; pt < cGroup.values.length; pt++) {\n                            cYBucket = yPtScale(cGroup.values[pt]);\n                            if (ptsObj.hasOwnProperty(cYBucket) !== true) {\n                                ptsObj[cYBucket] = [];\n                            }\n                            ptsObj[cYBucket].push(cPlot.objs.points.pts[pt]\n                                .attr(\"cx\", swarmBounds.middle)\n                                .attr(\"cy\", yPtScale(cGroup.values[pt]) * dOpts.pointSize));\n                        }\n                        //  Plot buckets\n                        var rightMax = Math.min(swarmBounds.right - dOpts.pointSize);\n                        for (var row in ptsObj) {\n                            var leftMin = swarmBounds.left + (Math.max((maxWidth - ptsObj[row].length) / 2, 0) * dOpts.pointSize);\n                            var col = 0;\n                            for (pt in ptsObj[row]) {\n                                ptsObj[row][pt].attr(\"cx\", Math.min(leftMin + col * dOpts.pointSize, rightMax) + dOpts.pointSize / 2);\n                                col++\n                            }\n                        }\n                    } else { // For scatter points and points with no scatter\n                        var plotBounds = null,\n                            scatterWidth = 0,\n                            width = 0;\n                        if (dOpts.plotType == 'scatter' || typeof dOpts.plotType == 'number') {\n                            //Default scatter percentage is 20% of box width\n                            scatterWidth = typeof dOpts.plotType == 'number' ? dOpts.plotType : 20;\n                        }\n\n                        plotBounds = getObjWidth(scatterWidth, cName);\n                        plotBounds.middle += chart.dataPlots.options.padding\n                        plotBounds.right += chart.dataPlots.options.padding\n                        plotBounds.left += chart.dataPlots.options.padding\n                        width = plotBounds.right - plotBounds.left;\n\n                        for (var pt = 0; pt < cGroup.values.length; pt++) {\n                            cPlot.objs.points.pts[pt]\n                                .attr(\"cx\", plotBounds.middle + addJitter(true, width))\n                                .attr(\"cy\", chart.yScale(cGroup.values[pt]));\n                        }\n                    }\n                }\n\n\n                if (cPlot.objs.bean) {\n                    var beanBounds = getObjWidth(dOpts.beanWidth, cName);\n                    for (var pt = 0; pt < cGroup.values.length; pt++) {\n                        cPlot.objs.bean.lines[pt]\n                            .attr(\"x1\", beanBounds.left)\n                            .attr(\"x2\", beanBounds.right)\n                            .attr('y1', chart.yScale(cGroup.values[pt]))\n                            .attr(\"y2\", chart.yScale(cGroup.values[pt]));\n                    }\n                }\n            }\n        };\n\n        /**\n         * Create the svg elements for the data plots\n         */\n        chart.dataPlots.preparePlots = function () {\n            var cName, cPlot;\n\n            if (dOpts && dOpts.colors) {\n                chart.dataPlots.colorFunct = getColorFunct(dOpts.colors);\n            } else {\n                chart.dataPlots.colorFunct = chart.colorFunct\n            }\n\n            if (dOpts.show == false) {\n                return\n            }\n\n            // Metrics lines\n            chart.dataPlots.objs.g = chart.objs.g.append(\"g\").attr(\"class\", \"metrics-lines\");\n            if (dOpts.showLines && dOpts.showLines.length > 0) {\n                chart.dataPlots.objs.lines = {};\n                var cMetric;\n                for (var line in dOpts.showLines) {\n                    cMetric = dOpts.showLines[line];\n                    chart.dataPlots.objs.lines[cMetric] = {};\n                    chart.dataPlots.objs.lines[cMetric].values = [];\n                    for (var cGroup in chart.groupObjs) {\n                        chart.dataPlots.objs.lines[cMetric].values.push({\n                            x: cGroup,\n                            y: chart.groupObjs[cGroup].metrics[cMetric]\n                        })\n                    }\n                    chart.dataPlots.objs.lines[cMetric].line = d3.svg.line()\n                        .interpolate(\"cardinal\")\n                        .y(function (d) {\n                            return chart.yScale(d.y)\n                        });\n                    chart.dataPlots.objs.lines[cMetric].g = chart.dataPlots.objs.g.append(\"path\")\n                        .attr(\"class\", \"line \" + cMetric)\n                        .attr(\"data-metric\", cMetric)\n                        .style(\"fill\", 'none')\n                        .style(\"stroke\", chart.colorFunct(cMetric));\n                }\n\n            }\n\n            for (cName in chart.groupObjs) {\n\n                cPlot = chart.groupObjs[cName].dataPlots;\n                cPlot.objs.g = chart.groupObjs[cName].g.append(\"g\").attr(\"class\", \"data-plot\");\n\n                // Points Plot\n                if (dOpts.showPlot) {\n                    cPlot.objs.points = {g: null, pts: []};\n                    cPlot.objs.points.g = cPlot.objs.g.append(\"g\").attr(\"class\", \"points-plot\");\n                    for (var pt = 0; pt < chart.groupObjs[cName].values.length; pt++) {\n                        cPlot.objs.points.pts.push(cPlot.objs.points.g\n                            .append(\"a\")\n                            .attr(\"xlink:href\", function(d) {\n                                return chart.groupObjs[cName].labels[pt] + \".html\"\n                            })\n                            .append(\"circle\")\n                            .attr(\"class\", \"point\")\n                            .attr('r', dOpts.pointSize / 2)// Options is diameter, r takes radius so divide by 2\n                            .style(\"fill\", chart.dataPlots.colorFunct(cName))\n                            .on(\"mouseover\", function() {\n                                chart.objs.tooltip\n                                    .style(\"display\", null)\n                                    .style(\"left\", (d3.event.pageX) + \"px\")\n                                    .style(\"top\", (d3.event.pageY - 28) + \"px\");\n                            })\n                            .on(\"mouseout\", function () {\n                                chart.objs.tooltip.style(\"display\", \"none\");\n                            })\n                            .on(\"mousemove\", pointHover(chart.groupObjs[cName].labels[pt], chart.groupObjs[cName].values[pt]))\n                        );\n                    }\n                }\n\n\n                // Bean lines\n                if (dOpts.showBeanLines) {\n                    cPlot.objs.bean = {g: null, lines: []};\n                    cPlot.objs.bean.g = cPlot.objs.g.append(\"g\").attr(\"class\", \"bean-plot\");\n                    for (var pt = 0; pt < chart.groupObjs[cName].values.length; pt++) {\n                        cPlot.objs.bean.lines.push(cPlot.objs.bean.g.append(\"line\")\n                            .attr(\"class\", \"bean line\")\n                            .style(\"stroke-width\", '1')\n                            .style(\"stroke\", chart.dataPlots.colorFunct(cName)));\n                    }\n                }\n            }\n\n        };\n        chart.dataPlots.preparePlots();\n\n        d3.select(window).on('resize.' + chart.selector + '.dataPlot', chart.dataPlots.update);\n        chart.dataPlots.update();\n        return chart;\n    };\n\n    return chart;\n}\n"
  },
  {
    "path": "mriqc/data/reports/group.html",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title></title>\n    <style type=\"text/css\">\n        {{ boxplots_css }}\n    </style>\n    <script type=\"text/javascript\">\n        {{ d3_js }}\n    </script>\n    <script type=\"text/javascript\">\n        {{ boxplots_js }}\n    </script>\n    <title>MRIQC: group {{ modality }}\nreport</title>\n</head>\n<body>\n    <h1>MRIQC: group {{ modality }}\nreport</h1>\n<h2>Summary</h2>\n<ul class=\"simple\">\n<li>Date and time: {{ timestamp }}.</li>\n<li>MRIQC version: {{ version }}.</li>\n{% if failed %}\n<li><span class=\"text-warning\">Some individual reports failed</span>:\n{% for f in failed %}\n<span class=\"text-warning\">{{ f }}</span>{% if loop.last %}.{% else %}, {% endif %}\n{% endfor %}\n</li>\n{% endif %}\n</ul>\n{% for data_csv in csv_groups %}\n<div class=\"chart-wrapper\" id=\"chart-group{{ loop.index0 }}\"></div>\n<pre class=\"csvdata\" id=\"data-csv{{ loop.index0 }}\" style=\"display: none\">\n{{ data_csv }}</pre>\n{% endfor %}\n\n\n<script type=\"text/javascript\">\n    var chart1;\n    function add(a, b) {\n        return a + b;\n    };\n\n    function plotData(group) {\n        data = d3.csv.parse(d3.select(\"#data-csv\" + group).text(), function(d) {\n            return {\n                iqm: d.iqm,\n                value: +d.value,\n                label: d.label,\n                units: d.units\n            }\n        });\n\n        var a = {};\n        var nIQMs = 0;\n        data.forEach(function(d) {\n            if(!a.hasOwnProperty(d.iqm)){\n                a[d.iqm] = 1\n                nIQMs += 1\n            }\n\n        });\n        if (data.length > 0) {\n            chart1 = makeDistroChart({\n                data: data,\n                xName: 'iqm',\n                yName: 'value',\n                selector: \"#chart-group\" + group,\n                chartSize: {height: 300, width: 100 + 60 * nIQMs},\n                constrainExtremes: false,\n                modality: '{{ modality }}'\n            });\n            chart1.renderDataPlots();\n            chart1.dataPlots.change({\n                showPlot: true,\n                plotType: 20,\n                padding: 20,\n                showBeanLines: false\n            });\n            chart1.renderBoxPlot();\n            chart1.boxPlots.show({\n                showWhiskers: true,\n                showOutliers: false,\n                boxWidth: 15,\n                lineWidth: 15,\n                padding: -10,\n                colors: ['#555']\n            });\n\n            // chart1.renderNotchBoxes({showNotchBox:false});\n            // chart1.renderViolinPlot({showViolinPlot:false});\n            // chart1.violinPlots.show({reset:true,clamp:0});\n            // chart1.dataPlots.show({showPlot:false,showBeanLines:true, colors:['#555']})\n\n            //chart1.notchBoxes.hide();\n            //chart1.boxPlots.hide();\n        }\n    }\n\n    {% for data_csv in csv_groups %}\n    plotData({{ loop.index0 }});\n    {% endfor %}\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "mriqc/data/reports/resources/DO_NOT_REMOVE_OR_MODIFY",
    "content": "Do not remove or modify the files in this folder. They are being served from\ngithub to render group level reports in a v0.9.0."
  },
  {
    "path": "mriqc/data/reports/resources/boxplots.css",
    "content": "/* Hide data table */\n.csvdata {\n    display: none;\n}\n\nbody {\n    font-family: helvetica;\n}\n\n.text-warning {\n    font-weight: bold;\n    color: red;\n}\n/*Primary Chart*/\n\n/*Nested divs for responsiveness*/\n.chart-wrapper {\n    max-width: 800px;  /*Overwritten by the JS*/\n    min-width: 160px;\n    margin-bottom: 20px;\n    font-family: helvetica;\n}\n.chart-wrapper .inner-wrapper {\n    position: relative;\n    padding-bottom: 50%; /*Overwritten by the JS*/\n    width: 100%;\n}\n.chart-wrapper .outer-box {\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n}\n.chart-wrapper .inner-box {\n    width: 100%;\n    height: 100%;\n}\n\n.chart-wrapper text {\n  font-family: helvetica;\n  font-size: 13px;\n}\n\n.chart-wrapper .axis path,\n.chart-wrapper .axis line {\n    fill: none;\n    stroke: #888;\n    stroke-width: 2px;\n    shape-rendering: crispEdges;\n}\n\n.chart-wrapper .y.axis .tick line {\n    stroke: lightgrey;\n    opacity: 0.6;\n    stroke-dasharray: 2,1;\n    stroke-width: 1;\n    shape-rendering: crispEdges;\n\n}\n\n.chart-wrapper .x.axis .domain {\n  display: none;\n}\n\n.chart-wrapper div.tooltip {\n    position: absolute;\n    text-align: left;\n    padding: 3px;\n    font-size: 12px;\n    background: #eee;\n    border: 0px;\n    border-radius: 1px;\n    pointer-events: none;\n    opacity: .7;\n    z-index: 10;\n}\n\n/*Box Plot*/\n.chart-wrapper .box-plot .box {\n    fill-opacity: .5;\n    stroke-width: 2;\n}\n.chart-wrapper .box-plot line {\n    stroke-width: 2px;\n}\n.chart-wrapper .box-plot circle {\n    fill: white;\n    stroke: black;\n}\n\n.chart-wrapper .box-plot .median {\n    stroke: black;\n}\n\n.chart-wrapper .box-plot circle.median {\n    /*the script makes the circles the same color as the box, you can override this in the js*/\n    fill: white !important;\n}\n\n.chart-wrapper .box-plot .mean {\n    stroke: white;\n    stroke-dasharray: 2,1;\n    stroke-width: 1px;\n}\n\n@media (max-width:500px){\n    .chart-wrapper .box-plot circle {display: none;}\n}\n\n/*Violin Plot*/\n\n.chart-wrapper .violin-plot .area {\n    shape-rendering: geometricPrecision;\n    opacity: 0.4;\n}\n\n.chart-wrapper .violin-plot .line {\n    fill: none;\n    stroke-width: 2px;\n    shape-rendering: geometricPrecision;\n}\n\n/*Notch Plot*/\n.chart-wrapper .notch-plot .notch {\n    fill-opacity: 0.4;\n    stroke-width: 2;\n}\n\n/* Point Plots*/\n.chart-wrapper .points-plot .point {\n    /*stroke: black;\n    stroke-width: 1px;*/\n    fill-opacity: 0.4;\n}\n\n.chart-wrapper .metrics-lines {\n    stroke-width: 4px;\n}\n\n/* Non-Chart Styles for demo*/\n.chart-options  {\n    min-width: 200px;\n    font-size: 13px;\n    font-family: helvetica;\n}\n.chart-options button {\n    margin: 3px;\n    padding: 3px;\n    font-size: 12px;\n}\n.chart-options p {\n    display: inline;\n}\n@media (max-width:500px){\n    .chart-options p {display: block;}\n}"
  },
  {
    "path": "mriqc/data/reports/resources/boxplots.js",
    "content": "/**\n * @fileOverview A D3 based distribution chart system. Supports: Box plots, Violin plots, Notched box plots, trend lines, beeswarm plot\n * @version 3.0\n */\n\n\n/**\n * Creates a box plot, violin plot, and or notched box plot\n * @param settings Configuration options for the base plot\n * @param settings.data The data for the plot\n * @param settings.xName The name of the column that should be used for the x groups\n * @param settings.yName The name of the column used for the y values\n * @param {string} settings.selector The selector string for the main chart div\n * @param [settings.axisLabels={}] Defaults to the xName and yName\n * @param [settings.yTicks = 1] 1 = default ticks. 2 =  double, 0.5 = half\n * @param [settings.scale='linear'] 'linear' or 'log' - y scale of the chart\n * @param [settings.chartSize={width:800, height:400}] The height and width of the chart itself (doesn't include the container)\n * @param [settings.margin={top: 15, right: 60, bottom: 40, left: 50}] The margins around the chart (inside the main div)\n * @param [settings.constrainExtremes=false] Should the y scale include outliers?\n * @returns {object} chart A chart object\n */\nfunction makeDistroChart(settings) {\n\n    var chart = {};\n\n    // Defaults\n    chart.settings = {\n        data: null,\n        xName: null,\n        yName: null,\n        axisLabels: {xAxis: null, yAxis: null},\n        labelName: \"label\",\n        unitsName: \"units\",\n        selector: null,\n        axisLables: null,\n        yTicks: 1,\n        scale: 'linear',\n        chartSize: {width: 800, height: 400},\n        margin: {top: 15, right: 10, bottom: 50, left: 50},\n        constrainExtremes: false,\n        color: d3.scale.category10(),\n        qctype: null\n    };\n\n    for (var setting in settings) {\n        chart.settings[setting] = settings[setting]\n    }\n\n    function formatAsFloat(d) {\n        if (d % 1 !== 0) {\n            return d3.format(\".2f\")(d);\n        } else {\n            return d3.format(\".0f\")(d);\n        }\n    }\n\n    function logFormatNumber(d) {\n        var x = Math.log(d) / Math.log(10) + 1e-6;\n        return Math.abs(x - Math.floor(x)) < 0.6 ? formatAsFloat(d) : \"\";\n    }\n\n    chart.yFormatter = formatAsFloat;\n\n    chart.data = chart.settings.data;\n\n    iqmName = chart.data[0][chart.settings.xName]\n    if (iqmName.lastIndexOf('_') > 0) {\n        iqmName = iqmName.substr(0, iqmName.lastIndexOf('_'))\n    }\n    chart.settings.axisLabels.yAxis = iqmName.toUpperCase()\n    units = chart.data[0][chart.settings.unitsName]\n    if (units) {\n        chart.settings.axisLabels.yAxis += ' (' + units + ')'\n    }\n\n\n    chart.groupObjs = {}; //The data organized by grouping and sorted as well as any metadata for the groups\n    chart.objs = {mainDiv: null, chartDiv: null, g: null, xAxis: null, yAxis: null};\n    chart.colorFunct = null;\n\n    /**\n     * Takes an array, function, or object mapping and created a color function from it\n     * @param {function|[]|object} colorOptions\n     * @returns {function} Function to be used to determine chart colors\n     */\n    function getColorFunct(colorOptions) {\n        if (typeof colorOptions == 'function') {\n            return colorOptions\n        } else if (Array.isArray(colorOptions)) {\n            //  If an array is provided, map it to the domain\n            var colorMap = {}, cColor = 0;\n            for (var cName in chart.groupObjs) {\n                colorMap[cName] = colorOptions[cColor];\n                cColor = (cColor + 1) % colorOptions.length;\n            }\n            return function (group) {\n                return colorMap[group];\n            }\n        } else if (typeof colorOptions == 'object') {\n            // if an object is provided, assume it maps to  the colors\n            return function (group) {\n                return colorOptions[group];\n            }\n        } else {\n            return d3.scale.category10();\n        }\n    }\n\n    /**\n     * Takes a percentage as returns the values that correspond to that percentage of the group range width\n     * @param objWidth Percentage of range band\n     * @param gName The bin name to use to get the x shift\n     * @returns {{left: null, right: null, middle: null}}\n     */\n    function getObjWidth(objWidth, gName) {\n        var objSize = {left: null, right: null, middle: null};\n        var width = chart.xScale.rangeBand() * (objWidth / 100);\n        var padding = (chart.xScale.rangeBand() - width) / 2;\n        var gShift = chart.xScale(gName);\n        objSize.middle = chart.xScale.rangeBand() / 2 + gShift;\n        objSize.left = padding + gShift;\n        objSize.right = objSize.left + width;\n        return objSize;\n    }\n\n    /**\n     * Adds jitter to the  scatter point plot\n     * @param doJitter true or false, add jitter to the point\n     * @param width percent of the range band to cover with the jitter\n     * @returns {number}\n     */\n    function addJitter(doJitter, width) {\n        if (doJitter !== true || width == 0) {\n            return 0\n        }\n        return Math.floor(Math.random() * width) - width / 2;\n    }\n\n    function shallowCopy(oldObj) {\n        var newObj = {};\n        for (var i in oldObj) {\n            if (oldObj.hasOwnProperty(i)) {\n                newObj[i] = oldObj[i];\n            }\n        }\n        return newObj;\n    }\n\n    /**\n     * Closure that creates the tooltip hover function\n     * @param groupName Name of the x group\n     * @param metrics Object to use to get values for the group\n     * @returns {Function} A function that provides the values for the tooltip\n     */\n    function tooltipHover(groupName, metrics) {\n        var tooltipString = \"Group: \" + groupName;\n        tooltipString += \"<br\\>Max: \" + formatAsFloat(metrics.max, 0.1);\n        tooltipString += \"<br\\>Q3: \" + formatAsFloat(metrics.quartile3);\n        tooltipString += \"<br\\>Median: \" + formatAsFloat(metrics.median);\n        tooltipString += \"<br\\>Q1: \" + formatAsFloat(metrics.quartile1);\n        tooltipString += \"<br\\>Min: \" + formatAsFloat(metrics.min);\n        return function () {\n            chart.objs.tooltip.transition().duration(200).style(\"opacity\", 0.9);\n            chart.objs.tooltip.html(tooltipString)\n        };\n    }\n\n    function axislabelHover(groupName) {\n        var tooltipString = \"Go to definition of \" + groupName;\n        return function () {\n            chart.objs.tooltip.transition().duration(200).style(\"opacity\", 1.0);\n            chart.objs.tooltip.html(tooltipString)\n        };\n    }\n\n    /**\n     * Closure that creates the tooltip hover function\n     * @param groupName Name of the x group\n     * @param metrics Object to use to get values for the group\n     * @returns {Function} A function that provides the values for the tooltip\n     */\n    function pointHover(label, value) {\n        var tooltipString = \"Subject: \" + label + \"<br\\>Measure: \" + value\n        return function () {\n            chart.objs.tooltip.transition().duration(200).style(\"opacity\", 1.0);\n            chart.objs.tooltip.html(tooltipString)\n        };\n    }\n\n    /**\n     * Parse the data and calculates base values for the plots\n     */\n    !function prepareData() {\n        function calcMetrics(values) {\n            // Do not reorder in-place\n            values = values.slice(0).sort(d3.ascending)\n\n            var metrics = { //These are the original non�scaled values\n                max: null,\n                upperOuterFence: null,\n                upperInnerFence: null,\n                quartile3: null,\n                median: null,\n                mean: null,\n                iqr: null,\n                quartile1: null,\n                lowerInnerFence: null,\n                lowerOuterFence: null,\n                min: null\n            };\n\n            metrics.min = d3.min(values);\n            metrics.quartile1 = d3.quantile(values, 0.25);\n            metrics.median = d3.median(values);\n            metrics.mean = d3.mean(values);\n            metrics.quartile3 = d3.quantile(values, 0.75);\n            metrics.max = d3.max(values);\n            metrics.iqr = metrics.quartile3 - metrics.quartile1;\n\n            //The inner fences are the closest value to the IQR without going past it (assumes sorted lists)\n            var LIF = metrics.quartile1 - (1.5 * metrics.iqr);\n            var UIF = metrics.quartile3 + (1.5 * metrics.iqr);\n            for (var i = 0; i <= values.length; i++) {\n                if (values[i] < LIF) {\n                    continue;\n                }\n                if (!metrics.lowerInnerFence && values[i] >= LIF) {\n                    metrics.lowerInnerFence = values[i];\n                    continue;\n                }\n                if (values[i] > UIF) {\n                    metrics.upperInnerFence = values[i - 1];\n                    break;\n                }\n            }\n\n\n            metrics.lowerOuterFence = metrics.quartile1 - (3 * metrics.iqr);\n            metrics.upperOuterFence = metrics.quartile3 + (3 * metrics.iqr);\n            if (!metrics.lowerInnerFence) {\n                metrics.lowerInnerFence = metrics.min;\n            }\n            if (!metrics.upperInnerFence) {\n                metrics.upperInnerFence = metrics.max;\n            }\n            return metrics\n        }\n\n        var current_x = null;\n        var current_y = null;\n        var current_row;\n\n        // Group the values\n        for (current_row = 0; current_row < chart.data.length; current_row++) {\n            current_x = chart.data[current_row][chart.settings.xName];\n            current_y = chart.data[current_row][chart.settings.yName];\n            current_label = chart.data[current_row][chart.settings.labelName];\n\n            if (chart.groupObjs.hasOwnProperty(current_x)) {\n                chart.groupObjs[current_x].values.push(current_y);\n                chart.groupObjs[current_x].labels.push(current_label);\n            } else {\n                chart.groupObjs[current_x] = {};\n                chart.groupObjs[current_x].values = [current_y];\n                chart.groupObjs[current_x].labels = [current_label];\n            }\n        }\n\n        for (var cName in chart.groupObjs) {\n            // chart.groupObjs[cName].values.sort(d3.ascending);\n            chart.groupObjs[cName].metrics = {};\n            chart.groupObjs[cName].metrics = calcMetrics(chart.groupObjs[cName].values);\n\n        }\n    }();\n\n    /**\n     * Prepare the chart settings and chart div and svg\n     */\n    !function prepareSettings() {\n        //Set base settings\n        chart.margin = chart.settings.margin;\n        chart.divWidth = chart.settings.chartSize.width;\n        chart.divHeight = chart.settings.chartSize.height;\n        chart.width = chart.divWidth - chart.margin.left - chart.margin.right;\n        chart.height = chart.divHeight - chart.margin.top - chart.margin.bottom;\n\n        if (chart.settings.axisLabels) {\n            chart.xAxisLable = chart.settings.axisLabels.xAxis;\n            chart.yAxisLable = chart.settings.axisLabels.yAxis;\n        }\n\n        if (chart.settings.scale === 'log') {\n            chart.yScale = d3.scale.log();\n            chart.yFormatter = logFormatNumber;\n        } else {\n            chart.yScale = d3.scale.linear();\n        }\n\n        if (chart.settings.constrainExtremes === true) {\n            var fences = [];\n            for (var cName in chart.groupObjs) {\n                fences.push(chart.groupObjs[cName].metrics.lowerInnerFence);\n                fences.push(chart.groupObjs[cName].metrics.upperInnerFence);\n            }\n            chart.range = d3.extent(fences);\n\n        } else {\n            chart.range = d3.extent(chart.data, function (d) {return d[chart.settings.yName];});\n        }\n\n        chart.colorFunct = getColorFunct(chart.settings.colors);\n\n        // Build Scale functions\n        chart.yScale.range([chart.height, 0]).domain(chart.range).nice().clamp(true);\n        chart.xScale = d3.scale.ordinal().domain(Object.keys(chart.groupObjs)).rangeBands([0, chart.width]);\n\n        //Build Axes Functions\n        chart.objs.yAxis = d3.svg.axis()\n            .scale(chart.yScale)\n            .orient(\"left\")\n            .tickFormat(chart.yFormatter)\n            .outerTickSize(0)\n            .innerTickSize(-chart.width + (chart.margin.right + chart.margin.left));\n        chart.objs.yAxis.ticks(chart.objs.yAxis.ticks()*chart.settings.yTicks);\n        chart.objs.xAxis = d3.svg.axis().scale(chart.xScale).orient(\"bottom\").tickSize(5);\n    }();\n\n    /**\n     * Updates the chart based on the current settings and window size\n     * @returns {*}\n     */\n    chart.update = function () {\n        // Update chart size based on view port size\n        chart.width = parseInt(chart.objs.chartDiv.style(\"width\"), 10) - (chart.margin.left + chart.margin.right);\n        chart.height = parseInt(chart.objs.chartDiv.style(\"height\"), 10) - (chart.margin.top + chart.margin.bottom);\n\n        // Update scale functions\n        chart.xScale.rangeBands([0, chart.width]);\n        chart.yScale.range([chart.height, 0]);\n\n        // Update the yDomain if the Violin plot clamp is set to -1 meaning it will extend the violins to make nice points\n        if (chart.violinPlots && chart.violinPlots.options.show == true && chart.violinPlots.options._yDomainVP != null) {\n            chart.yScale.domain(chart.violinPlots.options._yDomainVP).nice().clamp(true);\n        } else {\n            chart.yScale.domain(chart.range).nice().clamp(true);\n        }\n\n        //Update axes\n        chart.objs.g.select('.x.axis').attr(\"transform\", \"translate(0,\" + chart.height + \")\").call(chart.objs.xAxis)\n            .selectAll(\"text\")\n            .attr(\"y\", 5)\n            .attr(\"x\", -5)\n            .attr(\"transform\", \"rotate(-45)\")\n            .style(\"text-anchor\", \"end\");\n        chart.objs.g.select('.x.axis .label').attr(\"x\", chart.width / 2);\n        chart.objs.g.select('.y.axis').call(chart.objs.yAxis.innerTickSize(-chart.width));\n        chart.objs.g.select('.y.axis .label').attr(\"x\", -chart.height / 2);\n        chart.objs.chartDiv.select('svg').attr(\"width\", chart.width + (chart.margin.left + chart.margin.right)).attr(\"height\", chart.height + (chart.margin.top + chart.margin.bottom));\n\n        return chart;\n    };\n\n    /**\n     * Prepare the chart html elements\n     */\n    !function prepareChart() {\n        // Build main div and chart div\n        chart.objs.mainDiv = d3.select(chart.settings.selector)\n            .style(\"width\", chart.divWidth + \"px\")\n            .style(\"display\", \"inline-block\");\n        // Add all the divs to make it centered and responsive\n        chart.objs.mainDiv.append(\"div\")\n            .attr(\"class\", \"inner-wrapper\")\n            .style(\"padding-bottom\", (chart.divHeight / chart.divWidth) * 100 + \"%\")\n            .append(\"div\").attr(\"class\", \"outer-box\")\n            .append(\"div\").attr(\"class\", \"inner-box\");\n        // Capture the inner div for the chart (where the chart actually is)\n        chart.selector = chart.settings.selector + \" .inner-box\";\n        chart.objs.chartDiv = d3.select(chart.selector);\n        d3.select(window).on('resize.' + chart.selector, chart.update);\n\n        // Create the svg\n        chart.objs.g = chart.objs.chartDiv.append(\"svg\")\n            .attr(\"class\", \"chart-area\")\n            .attr(\"width\", chart.width + (chart.margin.left + chart.margin.right))\n            .attr(\"height\", chart.height + (chart.margin.top + chart.margin.bottom))\n            .append(\"g\")\n            .attr(\"transform\", \"translate(\" + chart.margin.left + \",\" + chart.margin.top + \")\");\n\n        // Create axes\n        chart.objs.axes = chart.objs.g.append(\"g\").attr(\"class\", \"axis\");\n        chart.objs.axes.append(\"g\")\n            .attr(\"class\", \"x axis\")\n            .attr(\"transform\", \"translate(0,\" + chart.height + \")\")\n            .call(chart.objs.xAxis);\n        chart.objs.axes.append(\"g\")\n            .attr(\"class\", \"y axis\")\n            .call(chart.objs.yAxis)\n            .append(\"text\")\n            //.attr(\"class\", \"label\")\n            .attr(\"transform\", \"rotate(-90)\")\n            //.attr(\"y\", -42)\n            .attr(\"y\", 6)\n            .attr(\"dy\", \".71em\")\n            //.attr(\"x\", -chart.height / 2)\n            .style(\"text-anchor\", \"end\")\n            .style(\"font-size\", \"16px\")\n            .append(\"a\")\n            .attr(\"xlink:href\", function(d) {\n                return \"http://mriqc.readthedocs.io/en/latest/measures.html\"\n            })\n            .text(chart.yAxisLable)\n            .on(\"mouseover\", function () {\n                chart.objs.tooltip\n                    .style(\"display\", null)\n                    .style(\"left\", (d3.event.pageX) + \"px\")\n                    .style(\"top\", (d3.event.pageY - 28) + \"px\");\n            }).on(\"mouseout\", function () {\n                chart.objs.tooltip.style(\"display\", \"none\");\n            }).on(\"mousemove\", axislabelHover(chart.yAxisLable));\n\n        // Create tooltip div\n        chart.objs.tooltip = chart.objs.mainDiv.append('div').attr('class', 'tooltip');\n        for (var cName in chart.groupObjs) {\n            chart.groupObjs[cName].g = chart.objs.g.append(\"g\").attr(\"class\", \"group\");\n        }\n        chart.update();\n    }();\n\n    /**\n     * Render a violin plot on the current chart\n     * @param options\n     * @param [options.showViolinPlot=true] True or False, show the violin plot\n     * @param [options.resolution=100 default]\n     * @param [options.bandwidth=10 default] May need higher bandwidth for larger data sets\n     * @param [options.width=50] The max percent of the group rangeBand that the violin can be\n     * @param [options.interpolation=''] How to render the violin\n     * @param [options.clamp=0 default]\n     *   0 = keep data within chart min and max, clamp once data = 0. May extend beyond data set min and max\n     *   1 = clamp at min and max of data set. Possibly no tails\n     *  -1 = extend chart axis to make room for data to interpolate to 0. May extend axis and data set min and max\n     * @param [options.colors=chart default] The color mapping for the violin plot\n     * @returns {*} The chart object\n     */\n    chart.renderViolinPlot = function (options) {\n        chart.violinPlots = {};\n\n        var defaultOptions = {\n            show: true,\n            showViolinPlot: true,\n            resolution: 100,\n            bandwidth: 20,\n            width: 50,\n            interpolation: 'cardinal',\n            clamp: 1,\n            colors: chart.colorFunct,\n            _yDomainVP: null // If the Violin plot is set to close all violin plots, it may need to extend the domain, that extended domain is stored here\n        };\n        chart.violinPlots.options = shallowCopy(defaultOptions);\n        for (var option in options) {\n            chart.violinPlots.options[option] = options[option]\n        }\n        var vOpts = chart.violinPlots.options;\n\n        // Create violin plot objects\n        for (var cName in chart.groupObjs) {\n            chart.groupObjs[cName].violin = {};\n            chart.groupObjs[cName].violin.objs = {};\n        }\n\n        /**\n         * Take a new set of options and redraw the violin\n         * @param updateOptions\n         */\n        chart.violinPlots.change = function (updateOptions) {\n            if (updateOptions) {\n                for (var key in updateOptions) {\n                    vOpts[key] = updateOptions[key]\n                }\n            }\n\n            for (var cName in chart.groupObjs) {\n                chart.groupObjs[cName].violin.objs.g.remove()\n            }\n\n            chart.violinPlots.prepareViolin();\n            chart.violinPlots.update();\n        };\n\n        chart.violinPlots.reset = function () {\n            chart.violinPlots.change(defaultOptions)\n        };\n        chart.violinPlots.show = function (opts) {\n            if (opts !== undefined) {\n                opts.show = true;\n                if (opts.reset) {\n                    chart.violinPlots.reset()\n                }\n            } else {\n                opts = {show: true};\n            }\n            chart.violinPlots.change(opts);\n\n        };\n\n        chart.violinPlots.hide = function (opts) {\n            if (opts !== undefined) {\n                opts.show = false;\n                if (opts.reset) {\n                    chart.violinPlots.reset()\n                }\n            } else {\n                opts = {show: false};\n            }\n            chart.violinPlots.change(opts);\n\n        };\n\n        /**\n         * Update the violin obj values\n         */\n        chart.violinPlots.update = function () {\n            var cName, cViolinPlot;\n\n            for (cName in chart.groupObjs) {\n                cViolinPlot = chart.groupObjs[cName].violin;\n\n                // Build the violins sideways, so use the yScale for the xScale and make a new yScale\n                var xVScale = chart.yScale.copy();\n\n\n                // Create the Kernel Density Estimator Function\n                cViolinPlot.kde = kernelDensityEstimator(eKernel(vOpts.bandwidth), xVScale.ticks(vOpts.resolution));\n                cViolinPlot.kdedata = cViolinPlot.kde(chart.groupObjs[cName].values);\n\n                var interpolateMax = chart.groupObjs[cName].metrics.max,\n                    interpolateMin = chart.groupObjs[cName].metrics.min;\n\n                if (vOpts.clamp == 0 || vOpts.clamp == -1) { //\n                    // When clamp is 0, calculate the min and max that is needed to bring the violin plot to a point\n                    // interpolateMax = the Minimum value greater than the max where y = 0\n                    interpolateMax = d3.min(cViolinPlot.kdedata.filter(function (d) {\n                        return (d.x > chart.groupObjs[cName].metrics.max && d.y == 0)\n                    }), function (d) {\n                        return d.x;\n                    });\n                    // interpolateMin = the Maximum value less than the min where y = 0\n                    interpolateMin = d3.max(cViolinPlot.kdedata.filter(function (d) {\n                        return (d.x < chart.groupObjs[cName].metrics.min && d.y == 0)\n                    }), function (d) {\n                        return d.x;\n                    });\n                    // If clamp is -1 we need to extend the axes so that the violins come to a point\n                    if (vOpts.clamp == -1) {\n                        kdeTester = eKernelTest(eKernel(vOpts.bandwidth), chart.groupObjs[cName].values);\n                        if (!interpolateMax) {\n                            var interMaxY = kdeTester(chart.groupObjs[cName].metrics.max);\n                            var interMaxX = chart.groupObjs[cName].metrics.max;\n                            var count = 25; // Arbitrary limit to make sure we don't get an infinite loop\n                            while (count > 0 && interMaxY != 0) {\n                                interMaxY = kdeTester(interMaxX);\n                                interMaxX += 1;\n                                count -= 1;\n                            }\n                            interpolateMax = interMaxX;\n                        }\n                        if (!interpolateMin) {\n                            var interMinY = kdeTester(chart.groupObjs[cName].metrics.min);\n                            var interMinX = chart.groupObjs[cName].metrics.min;\n                            var count = 25;  // Arbitrary limit to make sure we don't get an infinite loop\n                            while (count > 0 && interMinY != 0) {\n                                interMinY = kdeTester(interMinX);\n                                interMinX -= 1;\n                                count -= 1;\n                            }\n                            interpolateMin = interMinX;\n                        }\n\n                    }\n                    // Check to see if the new values are outside the existing chart range\n                    //   If they are assign them to the master _yDomainVP\n                    if (!vOpts._yDomainVP) vOpts._yDomainVP = chart.range.slice(0);\n                    if (interpolateMin && interpolateMin < vOpts._yDomainVP[0]) {\n                        vOpts._yDomainVP[0] = interpolateMin;\n                    }\n                    if (interpolateMax && interpolateMax > vOpts._yDomainVP[1]) {\n                        vOpts._yDomainVP[1] = interpolateMax;\n                    }\n\n\n                }\n\n\n                if (vOpts.showViolinPlot) {\n                    chart.update();\n                    xVScale = chart.yScale.copy();\n\n                    // Need to recalculate the KDE because the xVScale changed\n                    cViolinPlot.kde = kernelDensityEstimator(eKernel(vOpts.bandwidth), xVScale.ticks(vOpts.resolution));\n                    cViolinPlot.kdedata = cViolinPlot.kde(chart.groupObjs[cName].values);\n                }\n\n                cViolinPlot.kdedata = cViolinPlot.kdedata\n                    .filter(function (d) {\n                        return (!interpolateMin || d.x >= interpolateMin)\n                    })\n                    .filter(function (d) {\n                        return (!interpolateMax || d.x <= interpolateMax)\n                    });\n            }\n            for (cName in chart.groupObjs) {\n                cViolinPlot = chart.groupObjs[cName].violin;\n\n                // Get the violin width\n                var objBounds = getObjWidth(vOpts.width, cName);\n                var width = (objBounds.right - objBounds.left) / 2;\n\n                var yVScale = d3.scale.linear()\n                    .range([width, 0])\n                    .domain([0, d3.max(cViolinPlot.kdedata, function (d) {return d.y;})])\n                    .clamp(true);\n\n                var area = d3.svg.area()\n                    .interpolate(vOpts.interpolation)\n                    .x(function (d) {return xVScale(d.x);})\n                    .y0(width)\n                    .y1(function (d) {return yVScale(d.y);});\n\n                var line = d3.svg.line()\n                    .interpolate(vOpts.interpolation)\n                    .x(function (d) {return xVScale(d.x);})\n                    .y(function (d) {return yVScale(d.y)});\n\n                if (cViolinPlot.objs.left.area) {\n                    cViolinPlot.objs.left.area\n                        .datum(cViolinPlot.kdedata)\n                        .attr(\"d\", area);\n                    cViolinPlot.objs.left.line\n                        .datum(cViolinPlot.kdedata)\n                        .attr(\"d\", line);\n\n                    cViolinPlot.objs.right.area\n                        .datum(cViolinPlot.kdedata)\n                        .attr(\"d\", area);\n                    cViolinPlot.objs.right.line\n                        .datum(cViolinPlot.kdedata)\n                        .attr(\"d\", line);\n                }\n\n                // Rotate the violins\n                cViolinPlot.objs.left.g.attr(\"transform\", \"rotate(90,0,0)   translate(0,-\" + objBounds.left + \")  scale(1,-1)\");\n                cViolinPlot.objs.right.g.attr(\"transform\", \"rotate(90,0,0)  translate(0,-\" + objBounds.right + \")\");\n            }\n        };\n\n        /**\n         * Create the svg elements for the violin plot\n         */\n        chart.violinPlots.prepareViolin = function () {\n            var cName, cViolinPlot;\n\n            if (vOpts.colors) {\n                chart.violinPlots.color = getColorFunct(vOpts.colors);\n            } else {\n                chart.violinPlots.color = chart.colorFunct\n            }\n\n            if (vOpts.show == false) {return}\n\n            for (cName in chart.groupObjs) {\n                cViolinPlot = chart.groupObjs[cName].violin;\n\n                cViolinPlot.objs.g = chart.groupObjs[cName].g.append(\"g\").attr(\"class\", \"violin-plot\");\n                cViolinPlot.objs.left = {area: null, line: null, g: null};\n                cViolinPlot.objs.right = {area: null, line: null, g: null};\n\n                cViolinPlot.objs.left.g = cViolinPlot.objs.g.append(\"g\");\n                cViolinPlot.objs.right.g = cViolinPlot.objs.g.append(\"g\");\n\n                if (vOpts.showViolinPlot !== false) {\n                    //Area\n                    cViolinPlot.objs.left.area = cViolinPlot.objs.left.g.append(\"path\")\n                        .attr(\"class\", \"area\")\n                        .style(\"fill\", chart.violinPlots.color(cName));\n                    cViolinPlot.objs.right.area = cViolinPlot.objs.right.g.append(\"path\")\n                        .attr(\"class\", \"area\")\n                        .style(\"fill\", chart.violinPlots.color(cName));\n\n                    //Lines\n                    cViolinPlot.objs.left.line = cViolinPlot.objs.left.g.append(\"path\")\n                        .attr(\"class\", \"line\")\n                        .attr(\"fill\", 'none')\n                        .style(\"stroke\", chart.violinPlots.color(cName));\n                    cViolinPlot.objs.right.line = cViolinPlot.objs.right.g.append(\"path\")\n                        .attr(\"class\", \"line\")\n                        .attr(\"fill\", 'none')\n                        .style(\"stroke\", chart.violinPlots.color(cName));\n                }\n\n            }\n\n        };\n\n\n        function kernelDensityEstimator(kernel, x) {\n            return function (sample) {\n                return x.map(function (x) {\n                    return {x:x, y:d3.mean(sample, function (v) {return kernel(x - v);})};\n                });\n            };\n        }\n\n        function eKernel(scale) {\n            return function (u) {\n                return Math.abs(u /= scale) <= 1 ? .75 * (1 - u * u) / scale : 0;\n            };\n        }\n\n        // Used to find the roots for adjusting violin axis\n        // Given an array, find the value for a single point, even if it is not in the domain\n        function eKernelTest(kernel, array) {\n            return function (testX) {\n                return d3.mean(array, function (v) {return kernel(testX - v);})\n            }\n        }\n\n        chart.violinPlots.prepareViolin();\n\n        d3.select(window).on('resize.' + chart.selector + '.violinPlot', chart.violinPlots.update);\n        chart.violinPlots.update();\n        return chart;\n    };\n\n    /**\n     * Render a box plot on the current chart\n     * @param options\n     * @param [options.show=true] Toggle the whole plot on and off\n     * @param [options.showBox=true] Show the box part of the box plot\n     * @param [options.showWhiskers=true] Show the whiskers\n     * @param [options.showMedian=true] Show the median line\n     * @param [options.showMean=false] Show the mean line\n     * @param [options.medianCSize=3] The size of the circle on the median\n     * @param [options.showOutliers=true] Plot outliers\n     * @param [options.boxwidth=30] The max percent of the group rangeBand that the box can be\n     * @param [options.lineWidth=boxWidth] The max percent of the group rangeBand that the line can be\n     * @param [options.outlierScatter=false] Spread out the outliers so they don't all overlap (in development)\n     * @param [options.outlierCSize=2] Size of the outliers\n     * @param [options.colors=chart default] The color mapping for the box plot\n     * @returns {*} The chart object\n     */\n    chart.renderBoxPlot = function (options) {\n        chart.boxPlots = {};\n\n        // Defaults\n        var defaultOptions = {\n            show: true,\n            showBox: true,\n            showWhiskers: true,\n            showMedian: true,\n            showMean: false,\n            medianCSize: 3.5,\n            showOutliers: true,\n            boxWidth: 30,\n            lineWidth: null,\n            scatterOutliers: false,\n            outlierCSize: 2.5,\n            colors: chart.colorFunct,\n            padding: 0\n        };\n        chart.boxPlots.options = shallowCopy(defaultOptions);\n        for (var option in options) {\n            chart.boxPlots.options[option] = options[option]\n        }\n        var bOpts = chart.boxPlots.options;\n\n        //Create box plot objects\n        for (var cName in chart.groupObjs) {\n            chart.groupObjs[cName].boxPlot = {};\n            chart.groupObjs[cName].boxPlot.objs = {};\n        }\n\n\n        /**\n         * Calculates all the outlier points for each group\n         */\n        !function calcAllOutliers() {\n\n            /**\n             * Create lists of the outliers for each content group\n             * @param cGroup The object to modify\n             * @return null Modifies the object in place\n             */\n            function calcOutliers(cGroup) {\n                var cExtremes = [];\n                var cOutliers = [];\n                var cOut, idx;\n                for (idx = 0; idx <= cGroup.values.length; idx++) {\n                    cOut = {value: cGroup.values[idx]};\n\n                    if (cOut.value < cGroup.metrics.lowerInnerFence) {\n                        if (cOut.value < cGroup.metrics.lowerOuterFence) {\n                            cExtremes.push(cOut);\n                        } else {\n                            cOutliers.push(cOut);\n                        }\n                    } else if (cOut.value > cGroup.metrics.upperInnerFence) {\n                        if (cOut.value > cGroup.metrics.upperOuterFence) {\n                            cExtremes.push(cOut);\n                        } else {\n                            cOutliers.push(cOut);\n                        }\n                    }\n                }\n                cGroup.boxPlot.objs.outliers = cOutliers;\n                cGroup.boxPlot.objs.extremes = cExtremes;\n            }\n\n            for (var cName in chart.groupObjs) {\n                calcOutliers(chart.groupObjs[cName]);\n            }\n        }();\n\n        /**\n         * Take updated options and redraw the box plot\n         * @param updateOptions\n         */\n        chart.boxPlots.change = function (updateOptions) {\n            if (updateOptions) {\n                for (var key in updateOptions) {\n                    bOpts[key] = updateOptions[key]\n                }\n            }\n\n            for (var cName in chart.groupObjs) {\n                chart.groupObjs[cName].boxPlot.objs.g.remove()\n            }\n            chart.boxPlots.prepareBoxPlot();\n            chart.boxPlots.update()\n        };\n\n        chart.boxPlots.reset = function () {\n            chart.boxPlots.change(defaultOptions)\n        };\n        chart.boxPlots.show = function (opts) {\n            if (opts !== undefined) {\n                opts.show = true;\n                if (opts.reset) {\n                    chart.boxPlots.reset()\n                }\n            } else {\n                opts = {show: true};\n            }\n            chart.boxPlots.change(opts)\n\n        };\n        chart.boxPlots.hide = function (opts) {\n            if (opts !== undefined) {\n                opts.show = false;\n                if (opts.reset) {\n                    chart.boxPlots.reset()\n                }\n            } else {\n                opts = {show: false};\n            }\n            chart.boxPlots.change(opts)\n        };\n\n        /**\n         * Update the box plot obj values\n         */\n        chart.boxPlots.update = function () {\n            var cName, cBoxPlot;\n\n            for (cName in chart.groupObjs) {\n                cBoxPlot = chart.groupObjs[cName].boxPlot;\n\n                // Get the box width\n                var objBounds = getObjWidth(bOpts.boxWidth, cName);\n                objBounds.middle += chart.boxPlots.options.padding\n                objBounds.right += chart.boxPlots.options.padding\n                objBounds.left += chart.boxPlots.options.padding\n                var width = (objBounds.right - objBounds.left);\n\n                var sMetrics = {}; //temp var for scaled (plottable) metric values\n                for (var attr in chart.groupObjs[cName].metrics) {\n                    sMetrics[attr] = null;\n                    sMetrics[attr] = chart.yScale(chart.groupObjs[cName].metrics[attr]);\n                }\n\n                // Box\n                if (cBoxPlot.objs.box) {\n                    cBoxPlot.objs.box\n                        .attr(\"x\", objBounds.left)\n                        .attr('width', width)\n                        .attr(\"y\", sMetrics.quartile3)\n                        .attr(\"rx\", 1)\n                        .attr(\"ry\", 1)\n                        .attr(\"height\", -sMetrics.quartile3 + sMetrics.quartile1)\n                }\n\n                // Lines\n                var lineBounds = null;\n                if (bOpts.lineWidth) {\n                    lineBounds = getObjWidth(bOpts.lineWidth, cName)\n                } else {\n                    lineBounds = objBounds\n                }\n\n                // Apply padding\n                lineBounds.middle += chart.boxPlots.options.padding\n                lineBounds.right += chart.boxPlots.options.padding\n                lineBounds.left += chart.boxPlots.options.padding\n\n                // --Whiskers\n                if (cBoxPlot.objs.upperWhisker) {\n                    cBoxPlot.objs.upperWhisker.fence\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', sMetrics.upperInnerFence)\n                        .attr(\"y2\", sMetrics.upperInnerFence);\n                    cBoxPlot.objs.upperWhisker.line\n                        .attr(\"x1\", lineBounds.middle)\n                        .attr(\"x2\", lineBounds.middle)\n                        .attr('y1', sMetrics.quartile3)\n                        .attr(\"y2\", sMetrics.upperInnerFence);\n\n                    cBoxPlot.objs.lowerWhisker.fence\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', sMetrics.lowerInnerFence)\n                        .attr(\"y2\", sMetrics.lowerInnerFence);\n                    cBoxPlot.objs.lowerWhisker.line\n                        .attr(\"x1\", lineBounds.middle)\n                        .attr(\"x2\", lineBounds.middle)\n                        .attr('y1', sMetrics.quartile1)\n                        .attr(\"y2\", sMetrics.lowerInnerFence);\n                }\n\n                // --Median\n                if (cBoxPlot.objs.median) {\n                    cBoxPlot.objs.median.line\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', sMetrics.median)\n                        .attr(\"y2\", sMetrics.median);\n                    cBoxPlot.objs.median.circle\n                        .attr(\"cx\", lineBounds.middle)\n                        .attr(\"cy\", sMetrics.median)\n                }\n\n                // --Mean\n                if (cBoxPlot.objs.mean) {\n                    cBoxPlot.objs.mean.line\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', sMetrics.mean)\n                        .attr(\"y2\", sMetrics.mean);\n                    cBoxPlot.objs.mean.circle\n                        .attr(\"cx\", lineBounds.middle)\n                        .attr(\"cy\", sMetrics.mean);\n                }\n\n                // Outliers\n\n                var pt;\n                if (cBoxPlot.objs.outliers) {\n                    for (pt in cBoxPlot.objs.outliers) {\n                        cBoxPlot.objs.outliers[pt].point\n                            .attr(\"cx\", objBounds.middle + addJitter(bOpts.scatterOutliers, width))\n                            .attr(\"cy\", chart.yScale(cBoxPlot.objs.outliers[pt].value));\n                    }\n                }\n                if (cBoxPlot.objs.extremes) {\n                    for (pt in cBoxPlot.objs.extremes) {\n                        cBoxPlot.objs.extremes[pt].point\n                            .attr(\"cx\", objBounds.middle + addJitter(bOpts.scatterOutliers, width))\n                            .attr(\"cy\", chart.yScale(cBoxPlot.objs.extremes[pt].value));\n                    }\n                }\n            }\n        };\n\n        /**\n         * Create the svg elements for the box plot\n         */\n        chart.boxPlots.prepareBoxPlot = function () {\n            var cName, cBoxPlot;\n\n            if (bOpts.colors) {\n                chart.boxPlots.colorFunct = getColorFunct(bOpts.colors);\n            } else {\n                chart.boxPlots.colorFunct = chart.colorFunct\n            }\n\n            if (bOpts.show == false) {\n                return\n            }\n\n            for (cName in chart.groupObjs) {\n                cBoxPlot = chart.groupObjs[cName].boxPlot;\n\n                cBoxPlot.objs.g = chart.groupObjs[cName].g.append(\"g\").attr(\"class\", \"box-plot\");\n\n                //Plot Box (default show)\n                if (bOpts.showBox) {\n                    cBoxPlot.objs.box = cBoxPlot.objs.g.append(\"rect\")\n                        .attr(\"class\", \"box\")\n                        .style(\"fill\", chart.boxPlots.colorFunct(cName))\n                        .style(\"stroke\", chart.boxPlots.colorFunct(cName))\n                        .on(\"mouseover\", function () {\n                            chart.objs.tooltip\n                                .style(\"display\", null)\n                                .style(\"left\", (d3.event.pageX) + \"px\")\n                                .style(\"top\", (d3.event.pageY - 28) + \"px\");\n                        }).on(\"mouseout\", function () {\n                            chart.objs.tooltip.style(\"display\", \"none\");\n                        }).on(\"mousemove\", tooltipHover(cName, chart.groupObjs[cName].metrics));\n                    //A stroke is added to the box with the group color, it is\n                    // hidden by default and can be shown through css with stroke-width\n                }\n\n                //Plot Median (default show)\n                if (bOpts.showMedian) {\n                    cBoxPlot.objs.median = {line: null, circle: null};\n                    cBoxPlot.objs.median.line = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"median\");\n                    cBoxPlot.objs.median.circle = cBoxPlot.objs.g.append(\"circle\")\n                        .attr(\"class\", \"median\")\n                        .attr('r', bOpts.medianCSize)\n                        .style(\"fill\", chart.boxPlots.colorFunct(cName));\n                }\n\n                // Plot Mean (default no plot)\n                if (bOpts.showMean) {\n                    cBoxPlot.objs.mean = {line: null, circle: null};\n                    cBoxPlot.objs.mean.line = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"mean\");\n                    cBoxPlot.objs.mean.circle = cBoxPlot.objs.g.append(\"circle\")\n                        .attr(\"class\", \"mean\")\n                        .attr('r', bOpts.medianCSize)\n                        .style(\"fill\", chart.boxPlots.colorFunct(cName));\n                }\n\n                // Plot Whiskers (default show)\n                if (bOpts.showWhiskers) {\n                    cBoxPlot.objs.upperWhisker = {fence: null, line: null};\n                    cBoxPlot.objs.lowerWhisker = {fence: null, line: null};\n                    cBoxPlot.objs.upperWhisker.fence = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"upper whisker\")\n                        .style(\"stroke\", chart.boxPlots.colorFunct(cName));\n                    cBoxPlot.objs.upperWhisker.line = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"upper whisker\")\n                        .style(\"stroke\", chart.boxPlots.colorFunct(cName));\n\n                    cBoxPlot.objs.lowerWhisker.fence = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"lower whisker\")\n                        .style(\"stroke\", chart.boxPlots.colorFunct(cName));\n                    cBoxPlot.objs.lowerWhisker.line = cBoxPlot.objs.g.append(\"line\")\n                        .attr(\"class\", \"lower whisker\")\n                        .style(\"stroke\", chart.boxPlots.colorFunct(cName));\n                }\n\n                // Plot outliers (default show)\n                if (bOpts.showOutliers) {\n                    if (!cBoxPlot.objs.outliers) calcAllOutliers();\n                    var pt;\n                    if (cBoxPlot.objs.outliers.length) {\n                        var outDiv = cBoxPlot.objs.g.append(\"g\").attr(\"class\", \"boxplot outliers\");\n                        for (pt in cBoxPlot.objs.outliers) {\n                            cBoxPlot.objs.outliers[pt].point = outDiv.append(\"circle\")\n                                .attr(\"class\", \"outlier\")\n                                .attr('r', bOpts.outlierCSize)\n                                .style(\"fill\", chart.boxPlots.colorFunct(cName));\n                        }\n                    }\n\n                    if (cBoxPlot.objs.extremes.length) {\n                        var extDiv = cBoxPlot.objs.g.append(\"g\").attr(\"class\", \"boxplot extremes\");\n                        for (pt in cBoxPlot.objs.extremes) {\n                            cBoxPlot.objs.extremes[pt].point = extDiv.append(\"circle\")\n                                .attr(\"class\", \"extreme\")\n                                .attr('r', bOpts.outlierCSize)\n                                .style(\"stroke\", chart.boxPlots.colorFunct(cName));\n                        }\n                    }\n                }\n\n\n            }\n        };\n        chart.boxPlots.prepareBoxPlot();\n\n        d3.select(window).on('resize.' + chart.selector + '.boxPlot', chart.boxPlots.update);\n        chart.boxPlots.update();\n        return chart;\n\n    };\n\n    /**\n     * Render a notched box on the current chart\n     * @param options\n     * @param [options.show=true] Toggle the whole plot on and off\n     * @param [options.showNotchBox=true] Show the notch box\n     * @param [options.showLines=false] Show lines at the confidence intervals\n     * @param [options.boxWidth=35] The width of the widest part of the box\n     * @param [options.medianWidth=20] The width of the narrowist part of the box\n     * @param [options.lineWidth=50] The width of the confidence interval lines\n     * @param [options.notchStyle=null] null=traditional style, 'box' cuts out the whole notch in right angles\n     * @param [options.colors=chart default] The color mapping for the notch boxes\n     * @returns {*} The chart object\n     */\n    chart.renderNotchBoxes = function (options) {\n        chart.notchBoxes = {};\n\n        //Defaults\n        var defaultOptions = {\n            show: true,\n            showNotchBox: true,\n            showLines: false,\n            boxWidth: 35,\n            medianWidth: 20,\n            lineWidth: 50,\n            notchStyle: null,\n            colors: null\n        };\n        chart.notchBoxes.options = shallowCopy(defaultOptions);\n        for (var option in options) {\n            chart.notchBoxes.options[option] = options[option]\n        }\n        var nOpts = chart.notchBoxes.options;\n\n        //Create notch objects\n        for (var cName in chart.groupObjs) {\n            chart.groupObjs[cName].notchBox = {};\n            chart.groupObjs[cName].notchBox.objs = {};\n        }\n\n        /**\n         * Makes the svg path string for a notched box\n         * @param cNotch Current notch box object\n         * @param notchBounds objBound object\n         * @returns {string} A string in the proper format for a svg polygon\n         */\n        function makeNotchBox(cNotch, notchBounds) {\n            var scaledValues = [];\n            if (nOpts.notchStyle == 'box') {\n                scaledValues = [\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile1)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.medianLeft, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.medianLeft, chart.yScale(cNotch.metrics.median)],\n                    [notchBounds.medianLeft, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile3)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile3)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.medianRight, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.medianRight, chart.yScale(cNotch.metrics.median)],\n                    [notchBounds.medianRight, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile1)]\n                ];\n            } else {\n                scaledValues = [\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile1)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.medianLeft, chart.yScale(cNotch.metrics.median)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile3)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile3)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.upperNotch)],\n                    [notchBounds.medianRight, chart.yScale(cNotch.metrics.median)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.lowerNotch)],\n                    [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile1)]\n                ];\n            }\n            return scaledValues.map(function (d) {\n                return [d[0], d[1]].join(\",\");\n            }).join(\" \");\n        }\n\n        /**\n         * Calculate the confidence intervals\n         */\n        !function calcNotches() {\n            var cNotch, modifier;\n            for (var cName in chart.groupObjs) {\n                cNotch = chart.groupObjs[cName];\n                modifier = (1.57 * (cNotch.metrics.iqr / Math.sqrt(cNotch.values.length)));\n                cNotch.metrics.upperNotch = cNotch.metrics.median + modifier;\n                cNotch.metrics.lowerNotch = cNotch.metrics.median - modifier;\n            }\n        }();\n\n        /**\n         * Take a new set of options and redraw the notch boxes\n         * @param updateOptions\n         */\n        chart.notchBoxes.change = function (updateOptions) {\n            if (updateOptions) {\n                for (var key in updateOptions) {\n                    nOpts[key] = updateOptions[key]\n                }\n            }\n\n            for (var cName in chart.groupObjs) {\n                chart.groupObjs[cName].notchBox.objs.g.remove()\n            }\n            chart.notchBoxes.prepareNotchBoxes();\n            chart.notchBoxes.update();\n        };\n\n        chart.notchBoxes.reset = function () {\n            chart.notchBoxes.change(defaultOptions)\n        };\n        chart.notchBoxes.show = function (opts) {\n            if (opts !== undefined) {\n                opts.show = true;\n                if (opts.reset) {\n                    chart.notchBoxes.reset()\n                }\n            } else {\n                opts = {show: true};\n            }\n            chart.notchBoxes.change(opts)\n        };\n        chart.notchBoxes.hide = function (opts) {\n            if (opts !== undefined) {\n                opts.show = false;\n                if (opts.reset) {\n                    chart.notchBoxes.reset()\n                }\n            } else {\n                opts = {show: false};\n            }\n            chart.notchBoxes.change(opts)\n        };\n\n        /**\n         * Update the notch box obj values\n         */\n        chart.notchBoxes.update = function () {\n            var cName, cGroup;\n\n            for (cName in chart.groupObjs) {\n                cGroup = chart.groupObjs[cName];\n\n                // Get the box size\n                var boxBounds = getObjWidth(nOpts.boxWidth, cName);\n                var medianBounds = getObjWidth(nOpts.medianWidth, cName);\n\n                var notchBounds = {\n                    boxLeft: boxBounds.left,\n                    boxRight: boxBounds.right,\n                    middle: boxBounds.middle,\n                    medianLeft: medianBounds.left,\n                    medianRight: medianBounds.right\n                };\n\n                // Notch Box\n                if (cGroup.notchBox.objs.notch) {\n                    cGroup.notchBox.objs.notch\n                        .attr(\"points\", makeNotchBox(cGroup, notchBounds));\n                }\n                if (cGroup.notchBox.objs.upperLine) {\n                    var lineBounds = null;\n                    if (nOpts.lineWidth) {\n                        lineBounds = getObjWidth(nOpts.lineWidth, cName)\n                    } else {\n                        lineBounds = objBounds\n                    }\n\n                    var confidenceLines = {\n                        upper: chart.yScale(cGroup.metrics.upperNotch),\n                        lower: chart.yScale(cGroup.metrics.lowerNotch)\n                    };\n                    cGroup.notchBox.objs.upperLine\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', confidenceLines.upper)\n                        .attr(\"y2\", confidenceLines.upper);\n                    cGroup.notchBox.objs.lowerLine\n                        .attr(\"x1\", lineBounds.left)\n                        .attr(\"x2\", lineBounds.right)\n                        .attr('y1', confidenceLines.lower)\n                        .attr(\"y2\", confidenceLines.lower);\n                }\n            }\n        };\n\n        /**\n         * Create the svg elements for the notch boxes\n         */\n        chart.notchBoxes.prepareNotchBoxes = function () {\n            var cName, cNotch;\n\n            if (nOpts && nOpts.colors) {\n                chart.notchBoxes.colorFunct = getColorFunct(nOpts.colors);\n            } else {\n                chart.notchBoxes.colorFunct = chart.colorFunct\n            }\n\n            if (nOpts.show == false) {\n                return\n            }\n\n            for (cName in chart.groupObjs) {\n                cNotch = chart.groupObjs[cName].notchBox;\n\n                cNotch.objs.g = chart.groupObjs[cName].g.append(\"g\").attr(\"class\", \"notch-plot\");\n\n                // Plot Box (default show)\n                if (nOpts.showNotchBox) {\n                    cNotch.objs.notch = cNotch.objs.g.append(\"polygon\")\n                        .attr(\"class\", \"notch\")\n                        .style(\"fill\", chart.notchBoxes.colorFunct(cName))\n                        .style(\"stroke\", chart.notchBoxes.colorFunct(cName));\n                    //A stroke is added to the notch with the group color, it is\n                    // hidden by default and can be shown through css with stroke-width\n                }\n\n                //Plot Confidence Lines (default hide)\n                if (nOpts.showLines) {\n                    cNotch.objs.upperLine = cNotch.objs.g.append(\"line\")\n                        .attr(\"class\", \"upper confidence line\")\n                        .style(\"stroke\", chart.notchBoxes.colorFunct(cName));\n\n                    cNotch.objs.lowerLine = cNotch.objs.g.append(\"line\")\n                        .attr(\"class\", \"lower confidence line\")\n                        .style(\"stroke\", chart.notchBoxes.colorFunct(cName));\n                }\n            }\n        };\n        chart.notchBoxes.prepareNotchBoxes();\n\n        d3.select(window).on('resize.' + chart.selector + '.notchBox', chart.notchBoxes.update);\n        chart.notchBoxes.update();\n        return chart;\n    };\n\n    /**\n     * Render a raw data in various forms\n     * @param options\n     * @param [options.show=true] Toggle the whole plot on and off\n     * @param [options.showPlot=false] True or false, show points\n     * @param [options.plotType='none'] Options: no scatter = (false or 'none'); scatter points= (true or [amount=% of width (default=10)]); beeswarm points = ('beeswarm')\n     * @param [options.pointSize=6] Diameter of the circle in pizels (not the radius)\n     * @param [options.showLines=['median']] Can equal any of the metrics lines\n     * @param [options.showbeanLines=false] Options: no lines = false\n     * @param [options.beanWidth=20] % width\n     * @param [options.colors=chart default]\n     * @returns {*} The chart object\n     *\n     */\n    chart.renderDataPlots = function (options) {\n        chart.dataPlots = {};\n\n\n        //Defaults\n        var defaultOptions = {\n            show: true,\n            showPlot: false,\n            plotType: 'none',\n            pointSize: 6,\n            showLines: false,//['median'],\n            showBeanLines: false,\n            beanWidth: 20,\n            colors: null,\n            padding: 0\n        };\n        chart.dataPlots.options = shallowCopy(defaultOptions);\n        for (var option in options) {\n            chart.dataPlots.options[option] = options[option]\n        }\n        var dOpts = chart.dataPlots.options;\n\n        //Create notch objects\n        for (var cName in chart.groupObjs) {\n            chart.groupObjs[cName].dataPlots = {};\n            chart.groupObjs[cName].dataPlots.objs = {};\n        }\n        // The lines don't fit into a group bucket so they live under the dataPlot object\n        chart.dataPlots.objs = {};\n\n        /**\n         * Take updated options and redraw the data plots\n         * @param updateOptions\n         */\n        chart.dataPlots.change = function (updateOptions) {\n            if (updateOptions) {\n                for (var key in updateOptions) {\n                    dOpts[key] = updateOptions[key]\n                }\n            }\n\n            chart.dataPlots.objs.g.remove();\n            for (var cName in chart.groupObjs) {\n                chart.groupObjs[cName].dataPlots.objs.g.remove()\n            }\n            chart.dataPlots.preparePlots();\n            chart.dataPlots.update()\n        };\n\n        chart.dataPlots.reset = function () {\n            chart.dataPlots.change(defaultOptions)\n        };\n        chart.dataPlots.show = function (opts) {\n            if (opts !== undefined) {\n                opts.show = true;\n                if (opts.reset) {\n                    chart.dataPlots.reset()\n                }\n            } else {\n                opts = {show: true};\n            }\n            chart.dataPlots.change(opts)\n        };\n        chart.dataPlots.hide = function (opts) {\n            if (opts !== undefined) {\n                opts.show = false;\n                if (opts.reset) {\n                    chart.dataPlots.reset()\n                }\n            } else {\n                opts = {show: false};\n            }\n            chart.dataPlots.change(opts)\n        };\n\n        /**\n         * Update the data plot obj values\n         */\n        chart.dataPlots.update = function () {\n            var cName, cGroup, cPlot;\n\n            // Metrics lines\n            if (chart.dataPlots.objs.g) {\n                var halfBand = chart.xScale.rangeBand() / 2; // find the middle of each band\n                for (var cMetric in chart.dataPlots.objs.lines) {\n                    chart.dataPlots.objs.lines[cMetric].line\n                        .x(function (d) {\n                            return chart.xScale(d.x) + halfBand\n                        });\n                    chart.dataPlots.objs.lines[cMetric].g\n                        .datum(chart.dataPlots.objs.lines[cMetric].values)\n                        .attr('d', chart.dataPlots.objs.lines[cMetric].line);\n                }\n            }\n\n\n            for (cName in chart.groupObjs) {\n                cGroup = chart.groupObjs[cName];\n                cPlot = cGroup.dataPlots;\n\n                if (cPlot.objs.points) {\n                    if (dOpts.plotType == 'beeswarm') {\n                        var swarmBounds = getObjWidth(100, cName);\n                        var yPtScale = chart.yScale.copy()\n                            .range([Math.floor(chart.yScale.range()[0] / dOpts.pointSize), 0])\n                            .interpolate(d3.interpolateRound)\n                            .domain(chart.yScale.domain());\n                        var maxWidth = Math.floor(chart.xScale.rangeBand() / dOpts.pointSize);\n                        var ptsObj = {};\n                        var cYBucket = null;\n                        //  Bucket points\n                        for (var pt = 0; pt < cGroup.values.length; pt++) {\n                            cYBucket = yPtScale(cGroup.values[pt]);\n                            if (ptsObj.hasOwnProperty(cYBucket) !== true) {\n                                ptsObj[cYBucket] = [];\n                            }\n                            ptsObj[cYBucket].push(cPlot.objs.points.pts[pt]\n                                .attr(\"cx\", swarmBounds.middle)\n                                .attr(\"cy\", yPtScale(cGroup.values[pt]) * dOpts.pointSize));\n                        }\n                        //  Plot buckets\n                        var rightMax = Math.min(swarmBounds.right - dOpts.pointSize);\n                        for (var row in ptsObj) {\n                            var leftMin = swarmBounds.left + (Math.max((maxWidth - ptsObj[row].length) / 2, 0) * dOpts.pointSize);\n                            var col = 0;\n                            for (pt in ptsObj[row]) {\n                                ptsObj[row][pt].attr(\"cx\", Math.min(leftMin + col * dOpts.pointSize, rightMax) + dOpts.pointSize / 2);\n                                col++\n                            }\n                        }\n                    } else { // For scatter points and points with no scatter\n                        var plotBounds = null,\n                            scatterWidth = 0,\n                            width = 0;\n                        if (dOpts.plotType == 'scatter' || typeof dOpts.plotType == 'number') {\n                            //Default scatter percentage is 20% of box width\n                            scatterWidth = typeof dOpts.plotType == 'number' ? dOpts.plotType : 20;\n                        }\n\n                        plotBounds = getObjWidth(scatterWidth, cName);\n                        plotBounds.middle += chart.dataPlots.options.padding\n                        plotBounds.right += chart.dataPlots.options.padding\n                        plotBounds.left += chart.dataPlots.options.padding\n                        width = plotBounds.right - plotBounds.left;\n\n                        for (var pt = 0; pt < cGroup.values.length; pt++) {\n                            cPlot.objs.points.pts[pt]\n                                .attr(\"cx\", plotBounds.middle + addJitter(true, width))\n                                .attr(\"cy\", chart.yScale(cGroup.values[pt]));\n                        }\n                    }\n                }\n\n\n                if (cPlot.objs.bean) {\n                    var beanBounds = getObjWidth(dOpts.beanWidth, cName);\n                    for (var pt = 0; pt < cGroup.values.length; pt++) {\n                        cPlot.objs.bean.lines[pt]\n                            .attr(\"x1\", beanBounds.left)\n                            .attr(\"x2\", beanBounds.right)\n                            .attr('y1', chart.yScale(cGroup.values[pt]))\n                            .attr(\"y2\", chart.yScale(cGroup.values[pt]));\n                    }\n                }\n            }\n        };\n\n        /**\n         * Create the svg elements for the data plots\n         */\n        chart.dataPlots.preparePlots = function () {\n            var cName, cPlot;\n\n            if (dOpts && dOpts.colors) {\n                chart.dataPlots.colorFunct = getColorFunct(dOpts.colors);\n            } else {\n                chart.dataPlots.colorFunct = chart.colorFunct\n            }\n\n            if (dOpts.show == false) {\n                return\n            }\n\n            // Metrics lines\n            chart.dataPlots.objs.g = chart.objs.g.append(\"g\").attr(\"class\", \"metrics-lines\");\n            if (dOpts.showLines && dOpts.showLines.length > 0) {\n                chart.dataPlots.objs.lines = {};\n                var cMetric;\n                for (var line in dOpts.showLines) {\n                    cMetric = dOpts.showLines[line];\n                    chart.dataPlots.objs.lines[cMetric] = {};\n                    chart.dataPlots.objs.lines[cMetric].values = [];\n                    for (var cGroup in chart.groupObjs) {\n                        chart.dataPlots.objs.lines[cMetric].values.push({\n                            x: cGroup,\n                            y: chart.groupObjs[cGroup].metrics[cMetric]\n                        })\n                    }\n                    chart.dataPlots.objs.lines[cMetric].line = d3.svg.line()\n                        .interpolate(\"cardinal\")\n                        .y(function (d) {\n                            return chart.yScale(d.y)\n                        });\n                    chart.dataPlots.objs.lines[cMetric].g = chart.dataPlots.objs.g.append(\"path\")\n                        .attr(\"class\", \"line \" + cMetric)\n                        .attr(\"data-metric\", cMetric)\n                        .style(\"fill\", 'none')\n                        .style(\"stroke\", chart.colorFunct(cMetric));\n                }\n\n            }\n\n\n            for (cName in chart.groupObjs) {\n\n                cPlot = chart.groupObjs[cName].dataPlots;\n                cPlot.objs.g = chart.groupObjs[cName].g.append(\"g\").attr(\"class\", \"data-plot\");\n\n                // Points Plot\n                if (dOpts.showPlot) {\n                    cPlot.objs.points = {g: null, pts: []};\n                    cPlot.objs.points.g = cPlot.objs.g.append(\"g\").attr(\"class\", \"points-plot\");\n                    for (var pt = 0; pt < chart.groupObjs[cName].values.length; pt++) {\n                        cPlot.objs.points.pts.push(cPlot.objs.points.g\n                            .append(\"a\")\n                            .attr(\"xlink:href\", function(d) {\n                                if (chart.settings[\"qctype\"].startsWith('anat')) {\n                                    return chart.groupObjs[cName].labels[pt] + \"_T1w.html\"\n                                } else if (chart.settings[\"qctype\"].startsWith('func')) {\n                                    return chart.groupObjs[cName].labels[pt] + \"_bold.html\"\n                                }\n\n                            })\n                            .append(\"circle\")\n                            .attr(\"class\", \"point\")\n                            .attr('r', dOpts.pointSize / 2)// Options is diameter, r takes radius so divide by 2\n                            .style(\"fill\", chart.dataPlots.colorFunct(cName))\n                            .on(\"mouseover\", function() {\n                                chart.objs.tooltip\n                                    .style(\"display\", null)\n                                    .style(\"left\", (d3.event.pageX) + \"px\")\n                                    .style(\"top\", (d3.event.pageY - 28) + \"px\");\n                            })\n                            .on(\"mouseout\", function () {\n                                chart.objs.tooltip.style(\"display\", \"none\");\n                            })\n                            .on(\"mousemove\", pointHover(chart.groupObjs[cName].labels[pt], chart.groupObjs[cName].values[pt]))\n                        );\n                    }\n                }\n\n\n                // Bean lines\n                if (dOpts.showBeanLines) {\n                    cPlot.objs.bean = {g: null, lines: []};\n                    cPlot.objs.bean.g = cPlot.objs.g.append(\"g\").attr(\"class\", \"bean-plot\");\n                    for (var pt = 0; pt < chart.groupObjs[cName].values.length; pt++) {\n                        cPlot.objs.bean.lines.push(cPlot.objs.bean.g.append(\"line\")\n                            .attr(\"class\", \"bean line\")\n                            .style(\"stroke-width\", '1')\n                            .style(\"stroke\", chart.dataPlots.colorFunct(cName)));\n                    }\n                }\n            }\n\n        };\n        chart.dataPlots.preparePlots();\n\n        d3.select(window).on('resize.' + chart.selector + '.dataPlot', chart.dataPlots.update);\n        chart.dataPlots.update();\n        return chart;\n    };\n\n    return chart;\n}\n"
  },
  {
    "path": "mriqc/data/testdata/group_T1w.tsv",
    "content": "bids_name\tcjv\tcnr\tefc\tfber\tfwhm_avg\tfwhm_x\tfwhm_y\tfwhm_z\ticvs_csf\ticvs_gm\ticvs_wm\tinu_med\tinu_range\tqi_1\tqi_2\trpve_csf\trpve_gm\trpve_wm\tsize_x\tsize_y\tsize_z\tsnr_csf\tsnr_gm\tsnr_total\tsnr_wm\tsnrd_csf\tsnrd_gm\tsnrd_total\tsnrd_wm\tspacing_x\tspacing_y\tspacing_z\tsummary_bg_k\tsummary_bg_mad\tsummary_bg_mean\tsummary_bg_median\tsummary_bg_n\tsummary_bg_p05\tsummary_bg_p95\tsummary_bg_stdv\tsummary_csf_k\tsummary_csf_mad\tsummary_csf_mean\tsummary_csf_median\tsummary_csf_n\tsummary_csf_p05\tsummary_csf_p95\tsummary_csf_stdv\tsummary_gm_k\tsummary_gm_mad\tsummary_gm_mean\tsummary_gm_median\tsummary_gm_n\tsummary_gm_p05\tsummary_gm_p95\tsummary_gm_stdv\tsummary_wm_k\tsummary_wm_mad\tsummary_wm_mean\tsummary_wm_median\tsummary_wm_n\tsummary_wm_p05\tsummary_wm_p95\tsummary_wm_stdv\ttpm_overlap_csf\ttpm_overlap_gm\ttpm_overlap_wm\twm2max\nsub-50137_T1w\t0.5954143604542232\t1.6638579105552882\t0.696013425223169\t7632.404958677687\t4.202703333333333\t4.04437\t4.57298\t3.99076\t0.26024785652744636\t0.38408797703315845\t0.35566416643939525\t0.6877774000167847\t0.38117922544479355\t0.0\t0.002295668304182044\t5.081276724413724\t4.990756260234296\t5.113134802367116\t208\t256\t176\t2.2937107725306363\t4.917917779273032\t5.414127031616626\t9.030752543046212\t10.197292417890393\t14.569689441911478\t15.229464400494718\t20.921411341682283\t1.0\t1.0\t1.0\t338.43701049804054\t0.0\t7.293577983070605\t0.0\t2905640.0\t0.0\t43.53645095974207\t29.419580835323824\t1.8447784153324047\t148.1785165093192\t442.67347918309935\t457.92001267150044\t467343.9921919382\t108.17194144427776\t741.1431271880865\t199.64135332045623\t-0.35434565463924894\t93.55264354645082\t663.4221231418237\t654.2670446671546\t689731.7462465676\t459.0615651831031\t912.2579122893512\t133.037313704897\t2.017341484779524\t76.27779483169759\t922.4739051832804\t939.4977170489728\t638689.2620031061\t723.2325619198382\t1061.9193829484284\t104.03307776196522\t0.24142527697392707\t0.5351267249526063\t0.5492788818473785\t0.4052328641526733\nsub-50152_T1w\t0.539219705574777\t1.9474863519286423\t0.6407575000929341\t6400.0\t3.538223004967018\t3.5068090149010525\t3.72369\t3.38417\t0.24603987215843207\t0.3997439898531474\t0.3542161379884206\t0.6055886149406433\t0.29949661493301377\t0.0\t0.007373185025089607\t5.331492928503499\t5.259937594008643\t5.372550867202498\t160\t239\t200\t2.8606218342450362\t7.6336456345923995\t6.389366594664442\t8.673832315155893\t25.101236197040226\t31.375238888160823\t33.55424426478266\t44.186257709146936\t1.100000023841858\t1.0\t1.0\t6.414540030448752\t0.0\t7.885408184004529\t0.0\t2096437.0\t0.0\t38.87942571938038\t14.239021165535616\t0.04906040355413355\t177.41835136541522\t525.5333175241614\t545.5612502843142\t344069.29238630924\t191.59568993747234\t808.0636241286993\t190.7139388176008\t-0.5716823144664929\t70.32748948461413\t686.3957146724873\t681.9231698960066\t559013.587992302\t545.7505366802216\t840.583026945591\t89.3311783915161\t-0.37782611205030214\t79.81300083569138\t933.452586370995\t960.363458275795\t495346.1196362858\t711.8304204493761\t1075.9795888960361\t110.71951289754341\t0.24415403242557018\t0.5240272610492305\t0.5342199967075586\t0.4752163650668765\nsub-50785_T1w\t0.7461341169008633\t1.3397828655659076\t0.7530480076783983\t-1.0\t3.499472057939973\t3.46213\t3.7065461738199192\t3.32974\t0.2670508857178007\t0.3771657734932426\t0.35578334078895674\t0.9645677804946899\t0.27349820435047145\t0.0\t0.0008989212653515643\t7.277978198643724\t7.159746223301595\t7.305039410577692\t256\t180\t256\t2.3324453804558374\t3.7071785736699265\t3.8942598735683753\t5.643155666579362\t6.224456192836066\t8.970749163248403\t9.610299796084766\t13.63569403216983\t1.0\t0.9999875426292419\t1.0\t579.4582204165845\t0.0\t2.7989928403802895\t0.0\t5080237.0\t0.0\t0.0\t41.37895519541185\t0.4214879703475489\t118.70755566859489\t386.1200298164586\t393.141785312444\t423407.57698828075\t97.19213696569204\t657.170887414366\t168.55328075295242\t-0.22318958644717712\t105.67941658296553\t572.463234916038\t566.5999136902392\t597994.0708619782\t330.8538051880896\t835.5335811562836\t152.83845347074578\t0.8425022802147404\t114.1628559875614\t847.082864043662\t861.2416779398918\t564092.3521048883\t579.310912258923\t1077.3695249855518\t152.6168983168376\t0.24019729935470005\t0.5355084318272595\t0.5571234404197298\t0.3494179683613571\nsub-51187_T1w\t0.6999440574229334\t1.229109740105771\t0.6068733970705132\t4668.127902703063\t3.5394978356516567\t4.25443345257453\t2.965211919410108\t3.398848134970332\t0.2632767380846025\t0.38411410306597654\t0.352609158849421\t0.8645452558994293\t0.659174335002899\t0.0\t0.002621822353053779\t6.232708393405123\t6.104914216952696\t6.241270182398587\t256\t132\t256\t1.9045372860088077\t3.542878319529499\t3.517728319831832\t5.105769353957191\t5.3904165105611055\t7.623761622201767\t8.185965301979987\t11.543717773177091\t0.8593999743461609\t1.5000007152557373\t0.8593999743461609\t643.4203151583965\t0.0\t12.526359964354464\t0.0\t2453296.0\t0.0\t63.92100214585662\t49.63941944985638\t19.631055549488405\t198.10879423073186\t395.1546922405064\t408.42967384681106\t355951.2030053217\t38.246628262102604\t723.3140679895878\t214.45056661846712\t18.305382974842022\t102.98229317978202\t587.9801981000472\t577.6493276059628\t519323.80381326505\t337.8612728342414\t879.2389240153134\t163.04504963273772\t10.92217792214386\t104.91053306594992\t844.5959841770807\t874.6628160998225\t476728.99321174936\t544.9421983994544\t1059.6339149996638\t171.30854100624651\t0.23533804206716807\t0.5463730577598676\t0.5594191519575634\t0.31519791549296117\n"
  },
  {
    "path": "mriqc/data/testdata/group_bold.tsv",
    "content": "bids_name\taor\taqi\tdummy_trs\tdvars_nstd\tdvars_std\tdvars_vstd\tefc\tfber\tfd_mean\tfd_num\tfd_perc\tfwhm_avg\tfwhm_x\tfwhm_y\tfwhm_z\tgcor\tgsr_x\tgsr_y\tsize_t\tsize_x\tsize_y\tsize_z\tsnr\tspacing_tr\tspacing_x\tspacing_y\tspacing_z\tsummary_bg_k\tsummary_bg_mad\tsummary_bg_mean\tsummary_bg_median\tsummary_bg_n\tsummary_bg_p05\tsummary_bg_p95\tsummary_bg_stdv\tsummary_fg_k\tsummary_fg_mad\tsummary_fg_mean\tsummary_fg_median\tsummary_fg_n\tsummary_fg_p05\tsummary_fg_p95\tsummary_fg_stdv\ttsnr\nsub-ds205s03_task-functionallocalizer_run-01_bold\t0.006865975609756098\t0.01166174756097561\t0\t25.30130857259259\t1.10068032382716\t1.0422690707407405\t0.5578\t899.5551\t0.26118389619646404\t32\t39.02439024390244\t2.43764\t2.3422275\t2.692425\t2.2782675\t0.0529285\t-0.012628363445401192\t0.055389221757650375\t82\t53\t53\t27\t4.266440439452425\t2.200000047683716\t4.0\t4.0\t4.0\t35.3128\t6.4186\t50.9335\t25.0\t54995.0\t19.0\t173.0\t77.1159\t1.403\t141.8145\t759.3992\t764.0\t20848.0\t428.0\t1042.0\t179.0677\t53.9325022965204\nsub-ds205s03_task-view_run-01_bold\t0.0055223913043478245\t0.013993418152173915\t0\t21.883337964175826\t1.0119163635164834\t0.9670224290109894\t0.5552\t948.5766\t0.16520736608810047\t18\t19.565217391304348\t2.3244433333333334\t2.2259025\t2.592175\t2.1552525\t0.0183055\t-0.008478646166622639\t0.04804554581642151\t92\t53\t53\t27\t4.254223958782439\t2.200000047683716\t4.0\t4.0\t4.0\t34.6153\t6.4461\t47.5813\t25.0\t54686.0\t19.0\t157.0\t67.2752\t1.6046\t136.8667\t771.5826\t779.0\t21157.0\t419.0\t1054.0\t183.1078\t52.836231373017654\nsub-ds205s03_task-view_run-02_bold\t0.0006475\t0.01044403706521739\t0\t24.326057035714285\t0.9611447131868133\t0.8934934421978024\t0.5573\t902.4783\t0.22854716046970905\t44\t47.82608695652174\t2.3925758333333333\t2.3057225\t2.642175\t2.22983\t0.329435\t-0.011322595179080963\t0.053197648376226425\t92\t53\t53\t27\t4.259133013458754\t2.200000047683716\t4.0\t4.0\t4.0\t35.3124\t7.4936\t49.9201\t26.0\t54896.0\t19.0\t170.0\t73.8075\t1.461\t140.8794\t762.1778\t767.0\t20947.0\t428.0\t1044.0\t180.0793\t42.17791306972504\nsub-ds205s07_task-functionallocalizer_run-01_bold\t0.00972736111111111\t0.008194284305555556\t0\t22.879393483661968\t1.1479378432394367\t1.0417766205633798\t0.541\t962.3474\t0.1550487344317312\t15\t20.833333333333332\t2.2521649999999998\t2.1681675\t2.39802\t2.1903075\t0.0291203\t-0.007456877268850803\t0.03371790051460266\t72\t53\t53\t27\t4.285941229350169\t2.200000047683716\t4.0\t4.0\t4.0\t40.9527\t6.1157\t42.9513\t23.0\t55908.0\t17.0\t145.0\t67.5711\t1.7787\t137.3466\t708.532\t714.0\t19935.0\t387.0\t951.0\t166.587\t64.71407310082577\nsub-ds205s07_task-view_run-01_bold\t0.00585054347826087\t0.015204289673913043\t0\t22.382260123296703\t0.9959491859340658\t0.9369370696703297\t0.5386\t972.983\t0.1520993052375479\t15\t16.304347826086957\t2.2514825000000003\t2.174985\t2.38338\t2.1960825\t0.0282586\t-0.006972935516387224\t0.032188184559345245\t92\t53\t53\t27\t4.313751528981998\t2.200000047683716\t4.0\t4.0\t4.0\t41.5907\t6.156\t41.8577\t23.0\t56005.0\t17.0\t139.0\t65.5813\t1.8801\t136.4316\t706.7871\t713.0\t19838.0\t394.0\t946.0\t165.2812\t50.42334433761425\nsub-ds205s07_task-view_run-02_bold\t0.0019870652173913047\t0.014770980326086956\t0\t22.60577535087912\t1.0123932324175822\t0.9372968517582421\t0.5404\t965.007\t0.15451170465747024\t18\t19.565217391304348\t2.2389508333333334\t2.1558375\t2.3787425\t2.1822725\t0.0275085\t-0.007005929946899414\t0.03411094844341278\t92\t53\t53\t27\t4.365633569269246\t2.200000047683716\t4.0\t4.0\t4.0\t38.59\t6.1883\t42.5556\t23.0\t55958.0\t17.0\t143.0\t65.669\t1.7675\t138.1076\t711.3101\t717.0\t19885.0\t400.0\t952.0\t164.2332\t54.05953362467699\nsub-ds205s09_task-view_acq-LR_run-01_bold\t0.005607945205479453\t0.02232912082191781\t0\t23.14330828208334\t1.043644975\t0.9879293138888892\t0.5131\t1196.7195\t0.3258302280827085\t45\t61.64383561643836\t2.0490825\t2.0376875\t2.24239\t1.86717\t0.0367789\t-0.004032289143651724\t0.020073510706424713\t73\t53\t53\t27\t5.010099661656575\t2.200000047683716\t4.0\t4.0\t4.0\t42.9673\t5.1654\t39.4401\t23.0\t57775.0\t18.0\t124.0\t58.7775\t2.6347\t119.7322\t783.8782\t805.0\t18068.0\t448.0\t993.0\t160.671\t54.77862358093262\nsub-ds205s09_task-view_acq-LR_run-02_bold\t0.006578767123287671\t0.023950454794520546\t0\t35.81903538416666\t1.5608953040277778\t1.6171545502777782\t0.5136\t1186.884\t0.3345943377548133\t47\t64.38356164383562\t2.054238333333333\t2.038455\t2.2515525\t1.8727075\t0.0322103\t-0.0038295735139399767\t0.020550260320305824\t73\t53\t53\t27\t5.029289310984106\t2.200000047683716\t4.0\t4.0\t4.0\t44.2484\t5.2449\t39.9385\t23.0\t57802.0\t18.0\t126.0\t59.851\t2.6451\t118.8398\t785.5312\t807.0\t18041.0\t451.0\t994.0\t160.4556\t50.310420989990234\nsub-ds205s09_task-view_acq-RL_run-01_bold\t0.04132\t0.05151956945205479\t0\t34.941844608194444\t1.1900752381944446\t1.11412176375\t0.5122\t1214.6777\t0.5699059645824405\t53\t72.6027397260274\t2.103911666666667\t2.0650525\t2.28995\t1.9567325\t0.0259984\t-0.003200419247150421\t0.01928347535431385\t73\t53\t53\t27\t5.178320854531057\t2.200000047683716\t4.0\t4.0\t4.0\t42.1191\t5.0721\t39.7567\t23.0\t57912.0\t18.0\t126.0\t60.2804\t2.5073\t116.7328\t789.3764\t810.0\t17931.0\t460.0\t993.0\t156.417\t36.2927360534668\n"
  },
  {
    "path": "mriqc/data/tests/ds000005/CHANGES",
    "content": "2.0.1 2016-10-21\n  \n  - Added authors to dataset_discription.json\n\n1.0.1 2016-02-18\n\n  -  Update orientation information in nifti header for improved left-right determination\n\n\n1.0.0 2011-10-06\n\n  -  initial release\n"
  },
  {
    "path": "mriqc/data/tests/ds000005/README",
    "content": "This dataset was obtained from the OpenfMRI project (http://www.openfmri.org).\nAccession #: ds005\nDescription: Mixed-gambles task\n\nPlease cite the following references if you use these data:\n\nTom, S.M., Fox, C.R., Trepel, C., Poldrack, R.A. (2007). The neural basis of loss aversion in decision-making under risk. Science, 315(5811):515-8\n\n\nRelease history:\n10/06/2011: initial release\n3/21/2013: Updated release with QA information\n2/18/2016: Update orientation information in nifti header for improved left-right determination\n\nThis dataset is made available under the Public Domain Dedication and License \nv1.0, whose full text can be found at \nhttps://opendatacommons.org/licenses/pddl/1-0/. \nWe hope that all users will follow the ODC Attribution/Share-Alike \nCommunity Norms (https://opendatacommons.org/norms/odc-by-sa/); \nin particular, while not legally required, we hope that all users \nof the data will acknowledge the OpenfMRI project and NSF Grant \nOCI-1131441 (R. Poldrack, PI) in any publications.\n"
  },
  {
    "path": "mriqc/data/tests/ds000005/dataset_description.json",
    "content": "{\n    \"BIDSVersion\": \"1.0.0rc4\",\n    \"License\": \"This dataset is made available under the Public Domain Dedication and License \\nv1.0, whose full text can be found at \\nhttp://www.opendatacommons.org/licenses/pddl/1.0/. \\n We hope that all users will follow the ODC Attribution/Share-Alike \\nCommunity Norms (http://www.opendatacommons.org/norms/odc-by-sa/); \\n in particular, while not legally required, we hope that all users \\nof the data will acknowledge the OpenfMRI project and NSF Grant \\nOCI-1131441 (R. Poldrack, PI) in any publications.\",\n    \"Name\": \"Mixed-gambles task\",\n    \"Authors\": [\"Tom, S.M.\", \"Fox, C.R.\", \"Trepel, C.\", \"Poldrack, R.A.\"],\n    \"ReferencesAndLinks\": \"Tom, S.M., Fox, C.R., Trepel, C., Poldrack, R.A. (2007). The neural basis of loss aversion in decision-making under risk. Science, 315(5811):515-8\"\n}\n"
  },
  {
    "path": "mriqc/data/tests/ds000005/participants.tsv",
    "content": "participant_id\tsex\tage\nsub-01\tM\t28\nsub-02\tF\t21\nsub-03\tF\t27\nsub-04\tM\t25\nsub-05\tF\t20\nsub-06\tM\t20\nsub-07\tF\t24\nsub-08\tM\t25\nsub-09\tF\t19\nsub-10\tM\t20\nsub-11\tM\t20\nsub-12\tM\t21\nsub-13\tF\t22\nsub-14\tF\t19\nsub-15\tF\t20\nsub-16\tM\t22\n"
  },
  {
    "path": "mriqc/data/tests/ds000005/sub-01/func/sub-01_task-mixedgamblestask_run-01_bold.json",
    "content": "{\n\t\"EchoTime\": 0.030\n}"
  },
  {
    "path": "mriqc/data/tests/ds000005/sub-01/func/sub-01_task-mixedgamblestask_run-01_events.tsv",
    "content": ""
  },
  {
    "path": "mriqc/data/tests/ds000005/sub-01/func/sub-01_task-mixedgamblestask_run-02_events.tsv",
    "content": ""
  },
  {
    "path": "mriqc/data/tests/ds000005/sub-01/func/sub-01_task-mixedgamblestask_run-03_events.tsv",
    "content": ""
  },
  {
    "path": "mriqc/data/tests/ds000005/task-mixedgamblestask_bold.json",
    "content": "{\n    \"RepetitionTime\": 2.0,\n    \"TaskName\": \"mixed-gambles task\"\n}"
  },
  {
    "path": "mriqc/data/tests/ds002785/dataset_description.json",
    "content": "{\n    \"Name\": \"MRIQC - MRI Quality Control\",\n    \"BIDSVersion\": \"1.4.0\",\n    \"DatasetType\": \"derivative\",\n    \"GeneratedBy\": [\n        {\n            \"Name\": \"MRIQC\",\n            \"Version\": \"23.1.0.dev9+g936aae7\",\n            \"CodeURL\": \"https://github.com/nipreps/mriqc/archive/23.1.0.dev9+g936aae7.tar.gz\"\n        }\n    ],\n    \"HowToAcknowledge\": \"Please cite our paper (https://doi.org/10.1371/journal.pone.0184661).\",\n    \"SourceDatasets\": [\n        {\n            \"URL\": \"https://doi.org/10.18112/openneuro.ds002785.v2.0.0\",\n            \"DOI\": \"10.18112/openneuro.ds002785.v2.0.0\"\n        }\n    ],\n    \"License\": \"CC0\"\n}"
  },
  {
    "path": "mriqc/data/tests/ds002785/sub-0017/anat/sub-0017_T1w.json",
    "content": "{\n  \"bids_meta\": {\n    \"AcquisitionNumber\": 4,\n    \"BidsifyVersion\": \"0.3.5\",\n    \"ConversionSoftware\": \"dcm2niix\",\n    \"ConversionSoftwareVersion\": \"v1.0.20181125  GCC6.3.0\",\n    \"EchoTime\": 0.00386,\n    \"ImageComments\": \"PIop\",\n    \"ImageOrientationPatientDICOM\": [\n      0.997822,\n      -0.0345204,\n      0.056213,\n      0.0465325,\n      0.972345,\n      -0.228868\n    ],\n    \"Manufacturer\": \"Philips\",\n    \"PatientPosition\": \"HFS\",\n    \"PhilipsRescaleIntercept\": 0,\n    \"PhilipsRescaleSlope\": 9.97753,\n    \"PhilipsScaleSlope\": 0.00757456,\n    \"ProtocolName\": \"WIPt13dSENSE\",\n    \"ReconMatrixPE\": 240,\n    \"RepetitionTime\": 0.008384,\n    \"SeriesDescription\": \"ImageMRSERIES\",\n    \"SeriesNumber\": 4,\n    \"UsePhilipsFloatNotDisplayScaling\": 1,\n    \"dataset\": \"<unset>\",\n    \"modality\": \"T1w\",\n    \"subject_id\": \"0017\"\n  },\n  \"cjv\": 0.2984226700776387,\n  \"cnr\": 4.416750094393364,\n  \"efc\": 0.7655080888081706,\n  \"fber\": -1.0,\n  \"fwhm_avg\": 3.8418100772965005,\n  \"fwhm_x\": 3.8904602318895,\n  \"fwhm_y\": 3.87533,\n  \"fwhm_z\": 3.75964,\n  \"icvs_csf\": 0.21427377046371318,\n  \"icvs_gm\": 0.45182687552401934,\n  \"icvs_wm\": 0.33389935401226745,\n  \"inu_med\": 0.6728797554969788,\n  \"inu_range\": 0.2371561408042907,\n  \"provenance\": {\n    \"md5sum\": \"488fdd8468ee6f229187a832f75df8d3\",\n    \"settings\": {\n      \"testing\": false\n    },\n    \"software\": \"mriqc\",\n    \"version\": \"23.1.0.dev9+g936aae7\",\n    \"warnings\": {\n      \"large_rot_frame\": true,\n      \"small_air_mask\": true\n    },\n    \"webapi_port\": null,\n    \"webapi_url\": \"https://mriqc.nimh.nih.gov/api/v1\"\n  },\n  \"qi_1\": 0.0,\n  \"qi_2\": 0.00017117314810884092,\n  \"rpve_csf\": 29.107574622378852,\n  \"rpve_gm\": 15.038577634337337,\n  \"rpve_wm\": 23.878723605719156,\n  \"size_x\": 240,\n  \"size_y\": 240,\n  \"size_z\": 220,\n  \"snr_csf\": 1.2982109108596243,\n  \"snr_gm\": 7.2667769595632645,\n  \"snr_total\": 9.787154109403472,\n  \"snr_wm\": 20.796474457787525,\n  \"snrd_csf\": 16.969258904499053,\n  \"snrd_gm\": 67.7554225958473,\n  \"snrd_total\": 66.84473903463918,\n  \"snrd_wm\": 115.80953560357119,\n  \"spacing_x\": 0.9999999403953552,\n  \"spacing_y\": 1.0,\n  \"spacing_z\": 1.0,\n  \"summary_bg_k\": 80.2823504456352,\n  \"summary_bg_mad\": 0.0,\n  \"summary_bg_mean\": 1.4847520366725901,\n  \"summary_bg_median\": 0.0,\n  \"summary_bg_n\": 94563.0,\n  \"summary_bg_p05\": 0.0,\n  \"summary_bg_p95\": 13.871251210570335,\n  \"summary_bg_stdv\": 5.657015981655371,\n  \"summary_csf_k\": 0.8963167168111004,\n  \"summary_csf_mad\": 108.8018859045977,\n  \"summary_csf_mean\": 173.34180474016256,\n  \"summary_csf_median\": 146.52730152010918,\n  \"summary_csf_n\": 13872.0,\n  \"summary_csf_p05\": 32.577903371304274,\n  \"summary_csf_p95\": 380.9685418305918,\n  \"summary_csf_stdv\": 112.8645729243335,\n  \"summary_gm_k\": 0.05707251778228173,\n  \"summary_gm_mad\": 78.93115183761164,\n  \"summary_gm_mean\": 586.1216061525913,\n  \"summary_gm_median\": 585.0590937528759,\n  \"summary_gm_n\": 31773.0,\n  \"summary_gm_p05\": 454.25905592925847,\n  \"summary_gm_p95\": 721.002239859849,\n  \"summary_gm_stdv\": 80.51023033684723,\n  \"summary_wm_k\": 0.7997106523111079,\n  \"summary_wm_mad\": 44.89661847643965,\n  \"summary_wm_mean\": 998.5774157832561,\n  \"summary_wm_median\": 999.9999904409051,\n  \"summary_wm_n\": 162663.0,\n  \"summary_wm_p05\": 917.7981742881238,\n  \"summary_wm_p95\": 1074.8754415176809,\n  \"summary_wm_stdv\": 48.084925097579145,\n  \"tpm_overlap_csf\": 0.16996463260514408,\n  \"tpm_overlap_gm\": 0.47678360376698237,\n  \"tpm_overlap_wm\": 0.5159290584796369,\n  \"wm2max\": 0.7163876204972095\n}"
  },
  {
    "path": "mriqc/data/tests/ds002785/sub-0042/anat/sub-0042_T1w.json",
    "content": "{\n  \"bids_meta\": {\n    \"AcquisitionNumber\": 4,\n    \"BidsifyVersion\": \"0.3.5\",\n    \"ConversionSoftware\": \"dcm2niix\",\n    \"ConversionSoftwareVersion\": \"v1.0.20181125  GCC6.3.0\",\n    \"EchoTime\": 0.00374,\n    \"ImageComments\": \"Piop\",\n    \"ImageOrientationPatientDICOM\": [\n      0.999933,\n      0.0107105,\n      -0.00429963,\n      -0.0108207,\n      0.999591,\n      -0.0264809\n    ],\n    \"Manufacturer\": \"Philips\",\n    \"PatientPosition\": \"HFS\",\n    \"PhilipsRescaleIntercept\": 0,\n    \"PhilipsRescaleSlope\": 9.6315,\n    \"PhilipsScaleSlope\": 0.00738806,\n    \"ProtocolName\": \"WIPt13dSENSE\",\n    \"ReconMatrixPE\": 240,\n    \"RepetitionTime\": 0.008172,\n    \"SeriesDescription\": \"ImageMRSERIES\",\n    \"SeriesNumber\": 4,\n    \"UsePhilipsFloatNotDisplayScaling\": 1,\n    \"dataset\": \"<unset>\",\n    \"modality\": \"T1w\",\n    \"subject_id\": \"0042\"\n  },\n  \"cjv\": 0.32579099188384514,\n  \"cnr\": 4.02557353023444,\n  \"efc\": 0.7552281551403719,\n  \"fber\": -1.0,\n  \"fwhm_avg\": 3.8609668158203454,\n  \"fwhm_x\": 3.8525202296281,\n  \"fwhm_y\": 4.07575,\n  \"fwhm_z\": 3.654630217832936,\n  \"icvs_csf\": 0.21199491574747537,\n  \"icvs_gm\": 0.42802186419053817,\n  \"icvs_wm\": 0.35998322006198646,\n  \"inu_med\": 0.6003086566925049,\n  \"inu_range\": 0.2056518077850341,\n  \"provenance\": {\n    \"md5sum\": \"f60ae921c83c8c7c9d37a4f1c24a8414\",\n    \"settings\": {\n      \"testing\": false\n    },\n    \"software\": \"mriqc\",\n    \"version\": \"23.1.0.dev9+g936aae7\",\n    \"warnings\": {\n      \"large_rot_frame\": true,\n      \"small_air_mask\": true\n    },\n    \"webapi_port\": null,\n    \"webapi_url\": \"https://mriqc.nimh.nih.gov/api/v1\"\n  },\n  \"qi_1\": 0.0,\n  \"qi_2\": 0.00011642773748205091,\n  \"rpve_csf\": 24.61195525783125,\n  \"rpve_gm\": 12.89228853719876,\n  \"rpve_wm\": 18.582825144785794,\n  \"size_x\": 240,\n  \"size_y\": 240,\n  \"size_z\": 220,\n  \"snr_csf\": 1.551612537932502,\n  \"snr_gm\": 6.919063648216913,\n  \"snr_total\": 10.14424221136297,\n  \"snr_wm\": 21.962050447939493,\n  \"snrd_csf\": 14.07344638138718,\n  \"snrd_gm\": 47.16267312978192,\n  \"snrd_total\": 46.49048740518072,\n  \"snrd_wm\": 78.23534270437307,\n  \"spacing_x\": 0.9999999403953552,\n  \"spacing_y\": 1.0,\n  \"spacing_z\": 0.9999999403953552,\n  \"summary_bg_k\": 107.19628229499303,\n  \"summary_bg_mad\": 0.0,\n  \"summary_bg_mean\": 1.733096121297343,\n  \"summary_bg_median\": 0.0,\n  \"summary_bg_n\": 60935.0,\n  \"summary_bg_p05\": 0.0,\n  \"summary_bg_p95\": 12.45789003930986,\n  \"summary_bg_stdv\": 8.373729660228735,\n  \"summary_csf_k\": 0.44779374386824866,\n  \"summary_csf_mad\": 124.78208741269273,\n  \"summary_csf_mean\": 198.5499556123926,\n  \"summary_csf_median\": 179.88198394328356,\n  \"summary_csf_n\": 16060.0,\n  \"summary_csf_p05\": 38.84103320967406,\n  \"summary_csf_p95\": 399.40312000438564,\n  \"summary_csf_stdv\": 115.92867365260699,\n  \"summary_gm_k\": -0.0073664596272493554,\n  \"summary_gm_mad\": 87.15471518415075,\n  \"summary_gm_mean\": 603.3587115234336,\n  \"summary_gm_median\": 602.8171764574945,\n  \"summary_gm_n\": 32923.0,\n  \"summary_gm_p05\": 459.3140401970595,\n  \"summary_gm_p95\": 747.6067990032956,\n  \"summary_gm_stdv\": 87.12277442167746,\n  \"summary_wm_k\": 0.6531345168735507,\n  \"summary_wm_mad\": 42.23651582001151,\n  \"summary_wm_mean\": 998.386644906707,\n  \"summary_wm_median\": 999.9774240627885,\n  \"summary_wm_n\": 224894.0,\n  \"summary_wm_p05\": 921.4768900945783,\n  \"summary_wm_p95\": 1070.5193785503507,\n  \"summary_wm_stdv\": 45.53195992383758,\n  \"tpm_overlap_csf\": 0.1480490429482059,\n  \"tpm_overlap_gm\": 0.45370841555632335,\n  \"tpm_overlap_wm\": 0.504539439915526,\n  \"wm2max\": 0.8015368092794501\n}"
  },
  {
    "path": "mriqc/data/tests/gh1086-ds004134.oracle",
    "content": "182\n"
  },
  {
    "path": "mriqc/data/tests/gh921-dmd-20220428-0.oracle",
    "content": "0"
  },
  {
    "path": "mriqc/data/tests/gh921-dmd-20230319-0.oracle",
    "content": "13"
  },
  {
    "path": "mriqc/engine/__init__.py",
    "content": ""
  },
  {
    "path": "mriqc/engine/plugin.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2022 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"A lightweight NiPype MultiProc execution plugin.\"\"\"\n\n# Import packages\nimport gc\nimport getpass\nimport multiprocessing as mp\nimport os\nimport sys\nimport uuid\nfrom concurrent.futures import ProcessPoolExecutor\nfrom copy import deepcopy\nfrom time import sleep, strftime, time\nfrom traceback import format_exception\n\nfrom nipype.utils.filemanip import crash2txt, savepkl\n\n\n# Run node\ndef run_node(node, updatehash, taskid):\n    \"\"\"\n    Execute node.run(), catch and log any errors and get a result.\n\n    Parameters\n    ----------\n    node : nipype Node instance\n        the node to run\n    updatehash : boolean\n        flag for updating hash\n    taskid : int\n        an identifier for this task\n    Returns\n    -------\n    result : dictionary\n        dictionary containing the node runtime results and stats\n\n    \"\"\"\n    # Init variables\n    result = {'result': None, 'traceback': None, 'taskid': taskid}\n\n    # Try and execute the node via node.run()\n    try:\n        result['result'] = node.run(updatehash=updatehash)\n    except (KeyboardInterrupt, SystemError, SystemExit):\n        raise\n    except:  # noqa: E722, intendedly catch all here\n        from mriqc import config\n\n        tb = format_exception(*sys.exc_info())\n        node._traceback = tb\n        result['result'] = node.result\n        result['traceback'] = tb\n\n        crashfile = report_crash(node, traceback=tb)\n        config.loggers.workflow.error(f'Node {node._id} (taskid={taskid}) crashed: {crashfile}')\n\n    # Return the result dictionary\n    return result\n\n\nclass PluginBase:\n    \"\"\"Base class for plugins.\"\"\"\n\n    def __init__(self, plugin_args=None):\n        \"\"\"Initialize plugin.\"\"\"\n        if plugin_args is None:\n            plugin_args = {}\n        self.plugin_args = plugin_args\n        self._config = None\n        self._status_callback = plugin_args.get('status_callback')\n\n    def run(self, graph, config, updatehash=False):\n        \"\"\"\n        Instruct the plugin to execute the workflow graph.\n\n        The core plugin member that should be implemented by\n        all plugins.\n\n        Parameters\n        ----------\n        graph :\n            a networkx, flattened :abbr:`DAG (Directed Acyclic Graph)`\n            to be executed\n        config : :obj:`~nipype.config`\n            a nipype.config object\n        updatehash : :obj:`bool`\n            whether cached nodes with stale hash should be just updated.\n\n        \"\"\"\n        raise NotImplementedError\n\n\nclass DistributedPluginBase(PluginBase):\n    \"\"\"\n    Execute workflow with a distribution engine.\n\n    Combinations of ``proc_done`` and ``proc_pending``:\n    +------------+---------------+--------------------------------+\n    | proc_done  | proc_pending  | outcome                        |\n    +============+===============+================================+\n    | True       | False         | Process is finished            |\n    +------------+---------------+--------------------------------+\n    | True       | True          | Process is currently being run |\n    +------------+---------------+--------------------------------+\n    | False      | False         | Process is queued              |\n    +------------+---------------+--------------------------------+\n    | False      | True          | INVALID COMBINATION            |\n    +------------+---------------+--------------------------------+\n\n    Attributes\n    ----------\n    procs : :obj:`list`\n        list (N) of underlying interface elements to be processed\n    proc_done : :obj:`numpy.ndarray`\n        a boolean numpy array (N,) signifying whether a process has been\n        submitted for execution\n    proc_pending : :obj:`numpy.ndarray`\n        a boolean numpy array (N,) signifying whether a\n        process is currently running.\n    depidx : :obj:`numpy.matrix`\n        a boolean matrix (NxN) storing the dependency structure across\n        processes. Process dependencies are derived from each column.\n\n    \"\"\"\n\n    def __init__(self, plugin_args=None):\n        \"\"\"Initialize runtime attributes to none.\"\"\"\n        super().__init__(plugin_args=plugin_args)\n        self.procs = None\n        self.depidx = None\n        self.refidx = None\n        self.mapnodes = None\n        self.mapnodesubids = None\n        self.proc_done = None\n        self.proc_pending = None\n        self.pending_tasks = []\n        self.max_jobs = self.plugin_args.get('max_jobs', None)\n\n    def _prerun_check(self, graph):\n        \"\"\"Stub method to validate/massage graph and nodes before running.\"\"\"\n\n    def _postrun_check(self):\n        \"\"\"Stub method to close any open resources.\"\"\"\n\n    def run(self, graph, config, updatehash=False):\n        \"\"\"Execute a pre-defined pipeline using distributed approaches.\"\"\"\n        import numpy as np\n\n        self._config = config\n        poll_sleep_secs = float(config['execution']['poll_sleep_duration'])\n\n        self._prerun_check(graph)\n        # Generate appropriate structures for worker-manager model\n        self._generate_dependency_list(graph)\n        self.mapnodes = []\n        self.mapnodesubids = {}\n        # setup polling - TODO: change to threaded model\n        notrun = []\n        errors = []\n\n        while not np.all(self.proc_done) or np.any(self.proc_pending):\n            loop_start = time()\n            toappend = []\n            # trigger callbacks for any pending results\n            while self.pending_tasks:\n                taskid, jobid = self.pending_tasks.pop()\n                try:\n                    result = self._get_result(taskid)\n                except Exception as exc:  # noqa: BLE001\n                    notrun.append(self._clean_queue(jobid, graph))\n                    errors.append(exc)\n                else:\n                    if result:\n                        if result['traceback']:\n                            notrun.append(self._clean_queue(jobid, graph, result=result))\n                            errors.append(''.join(result['traceback']))\n                        else:\n                            self._task_finished_cb(jobid)\n                            self._remove_node_dirs()\n                        self._clear_task(taskid)\n                    else:\n                        if not (self.proc_done[jobid] and self.proc_pending[jobid]):\n                            raise RuntimeError(\n                                f'Plugin error while appending task <{taskid}> with ID {jobid}.'\n                            )\n                        toappend.insert(0, (taskid, jobid))\n\n            if toappend:\n                self.pending_tasks.extend(toappend)\n\n            num_jobs = len(self.pending_tasks)\n            if self.max_jobs is None or num_jobs < self.max_jobs:\n                self._send_procs_to_workers(updatehash=updatehash, graph=graph)\n\n            sleep_til = loop_start + poll_sleep_secs\n            sleep(max(0, sleep_til - time()))\n\n        self._remove_node_dirs()\n\n        # close any open resources\n        self._postrun_check()\n\n        if errors:\n            # If one or more nodes failed, re-rise first of them\n            error, cause = errors[0], None\n            if isinstance(error, str):\n                error = RuntimeError(error)\n\n            if len(errors) > 1:\n                error, cause = (\n                    RuntimeError(f'{len(errors)} raised. Re-raising first.'),\n                    error,\n                )\n\n            raise error from cause\n\n    def _get_result(self, taskid):\n        raise NotImplementedError\n\n    def _submit_job(self, node, updatehash=False):\n        raise NotImplementedError\n\n    def _clear_task(self, taskid):\n        raise NotImplementedError\n\n    def _clean_queue(self, jobid, graph, result=None):\n        from mriqc import config\n\n        if self._status_callback:\n            self._status_callback(self.procs[jobid], 'exception')\n        if result is None:\n            result = {\n                'result': None,\n                'traceback': '\\n'.join(format_exception(*sys.exc_info())),\n            }\n\n        if config.nipype.stop_on_first_crash:\n            raise RuntimeError(''.join(result['traceback']))\n        if jobid in self.mapnodesubids:\n            # remove current jobid\n            self.proc_pending[jobid] = False\n            self.proc_done[jobid] = True\n            # remove parent mapnode\n            jobid = self.mapnodesubids[jobid]\n            self.proc_pending[jobid] = False\n            self.proc_done[jobid] = True\n        # remove dependencies from queue\n        return self._remove_node_deps(jobid, graph)\n\n    def _send_procs_to_workers(self, updatehash=False, graph=None):\n        \"\"\"Submit tasks to workers when system resources are available.\"\"\"\n\n    def _submit_mapnode(self, jobid):\n        import numpy as np\n        import scipy.sparse as ssp\n\n        if jobid in self.mapnodes:\n            return True\n        self.mapnodes.append(jobid)\n        mapnodesubids = self.procs[jobid].get_subnodes()\n        numnodes = len(mapnodesubids)\n        for i in range(numnodes):\n            self.mapnodesubids[self.depidx.shape[0] + i] = jobid\n        self.procs.extend(mapnodesubids)\n        self.depidx = ssp.vstack(\n            (self.depidx, ssp.lil_matrix(np.zeros((numnodes, self.depidx.shape[1])))),\n            'lil',\n        )\n        self.depidx = ssp.hstack(\n            (self.depidx, ssp.lil_matrix(np.zeros((self.depidx.shape[0], numnodes)))),\n            'lil',\n        )\n        self.depidx[-numnodes:, jobid] = 1\n        self.proc_done = np.concatenate((self.proc_done, np.zeros(numnodes, dtype=bool)))\n        self.proc_pending = np.concatenate((self.proc_pending, np.zeros(numnodes, dtype=bool)))\n        return False\n\n    def _local_hash_check(self, jobid, graph):\n        from mriqc import config\n\n        if not config.nipype.local_hash_check:\n            return False\n\n        try:\n            cached, updated = self.procs[jobid].is_cached()\n        except Exception:\n            return False\n\n        overwrite = self.procs[jobid].overwrite\n        always_run = self.procs[jobid].interface.always_run\n\n        if cached and updated and (overwrite is False or overwrite is None and not always_run):\n            try:\n                self._task_finished_cb(jobid, cached=True)\n                self._remove_node_dirs()\n            except Exception:\n                self._clean_queue(jobid, graph)\n                self.proc_pending[jobid] = False\n            return True\n        return False\n\n    def _task_finished_cb(self, jobid, cached=False):\n        \"\"\"\n        Extract outputs and assign to inputs of dependent tasks.\n\n        This is called when a job is completed.\n        \"\"\"\n        if self._status_callback:\n            self._status_callback(self.procs[jobid], 'end')\n        # Update job and worker queues\n        self.proc_pending[jobid] = False\n        # update the job dependency structure\n        rowview = self.depidx.getrowview(jobid)\n        rowview[rowview.nonzero()] = 0\n        if jobid not in self.mapnodesubids:\n            try:\n                self.refidx[self.refidx[:, jobid].nonzero()[0], jobid] = 0\n            except NotImplementedError:\n                self.refidx[self.refidx[:, [jobid]].nonzero()[0], jobid] = 0\n\n    def _generate_dependency_list(self, graph):\n        \"\"\"Generate a dependency list for a list of graphs.\"\"\"\n        import numpy as np\n        from nipype.pipeline.engine.utils import topological_sort\n\n        try:\n            from networkx import to_scipy_sparse_array\n        except ImportError:  # NetworkX < 2.7\n            from networkx import to_scipy_sparse_matrix as to_scipy_sparse_array\n\n        self.procs, _ = topological_sort(graph)\n        self.depidx = to_scipy_sparse_array(graph, nodelist=self.procs, format='lil')\n        self.refidx = self.depidx.astype(int)\n        self.proc_done = np.zeros(len(self.procs), dtype=bool)\n        self.proc_pending = np.zeros(len(self.procs), dtype=bool)\n\n    def _remove_node_deps(self, jobid, graph):\n        import networkx as nx\n\n        try:\n            dfs_preorder = nx.dfs_preorder\n        except AttributeError:\n            dfs_preorder = nx.dfs_preorder_nodes\n        subnodes = list(dfs_preorder(graph, self.procs[jobid]))\n        for node in subnodes:\n            idx = self.procs.index(node)\n            self.proc_done[idx] = True\n            self.proc_pending[idx] = False\n        return {\n            'node': self.procs[jobid],\n            'dependents': subnodes,\n        }\n\n    def _remove_node_dirs(self):\n        \"\"\"Remove directories whose outputs have already been used up.\"\"\"\n        from shutil import rmtree\n\n        import numpy as np\n\n        from mriqc import config\n\n        if config.nipype.remove_node_directories:\n            indices = np.nonzero((self.refidx.sum(axis=1) == 0).__array__())[0]\n            for idx in indices:\n                if idx in self.mapnodesubids:\n                    continue\n                if self.proc_done[idx] and (not self.proc_pending[idx]):\n                    self.refidx[idx, idx] = -1\n                    outdir = self.procs[idx].output_dir()\n                    rmtree(outdir)\n\n\nclass MultiProcPlugin(DistributedPluginBase):\n    \"\"\"\n    A lightweight re-implementation of NiPype's MultiProc plugin.\n\n    Execute workflow with multiprocessing, not sending more jobs at once\n    than the system can support.\n    The plugin_args input to run can be used to control the multiprocessing\n    execution and defining the maximum amount of memory and threads that\n    should be used. When those parameters are not specified,\n    the number of threads and memory of the system is used.\n    System consuming nodes should be tagged::\n      memory_consuming_node.mem_gb = 8\n      thread_consuming_node.n_procs = 16\n\n    The default number of threads and memory are set at node\n    creation, and are 1 and 0.25GB respectively.\n    Currently supported options are:\n    - non_daemon: boolean flag to execute as non-daemon processes\n    - n_procs: maximum number of threads to be executed in parallel\n    - memory_gb: maximum memory (in GB) that can be used at once.\n    - raise_insufficient: raise error if the requested resources for\n        a node over the maximum `n_procs` and/or `memory_gb`\n        (default is ``True``).\n    - scheduler: sort jobs topologically (``'tsort'``, default value)\n        or prioritize jobs by, first, memory consumption and, second,\n        number of threads (``'mem_thread'`` option).\n    - mp_context: name of multiprocessing context to use\n    \"\"\"\n\n    def __init__(self, pool=None, plugin_args=None):\n        \"\"\"Initialize the plugin.\"\"\"\n        from mriqc import config\n\n        super().__init__(plugin_args=plugin_args)\n        self._taskresult = {}\n        self._task_obj = {}\n        self._taskid = 0\n\n        # Cache current working directory and make sure we\n        # change to it when workers are set up\n        self._cwd = os.getcwd()\n\n        # Read in options or set defaults.\n        self.processors = self.plugin_args.get('n_procs', mp.cpu_count())\n        self.memory_gb = self.plugin_args.get(\n            'memory_gb',  # Allocate 90% of system memory\n            config.environment.total_memory * 0.9,\n        )\n        self.raise_insufficient = self.plugin_args.get('raise_insufficient', False)\n\n        # Instantiate different thread pools for non-daemon processes\n        mp_context = mp.get_context(self.plugin_args.get('mp_context'))\n        self.pool = pool or ProcessPoolExecutor(\n            max_workers=self.processors,\n            initializer=config._process_initializer,\n            initargs=(config.settings.file_path,),\n            mp_context=mp_context,\n        )\n\n        self._stats = None\n\n    def _async_callback(self, args):\n        result = args.result()\n        self._taskresult[result['taskid']] = result\n\n    def _get_result(self, taskid):\n        return self._taskresult.get(taskid)\n\n    def _clear_task(self, taskid):\n        del self._task_obj[taskid]\n\n    def _submit_job(self, node, updatehash=False):\n        self._taskid += 1\n\n        # Don't allow streaming outputs\n        if getattr(node.interface, 'terminal_output', '') == 'stream':\n            node.interface.terminal_output = 'allatonce'\n\n        result_future = self.pool.submit(run_node, node, updatehash, self._taskid)\n        result_future.add_done_callback(self._async_callback)\n        self._task_obj[self._taskid] = result_future\n        return self._taskid\n\n    def _prerun_check(self, graph):\n        \"\"\"Check if any node exceeds the available resources.\"\"\"\n        import numpy as np\n\n        tasks_mem_gb = []\n        tasks_num_th = []\n        for node in graph.nodes():\n            tasks_mem_gb.append(node.mem_gb)\n            tasks_num_th.append(node.n_procs)\n\n        if self.raise_insufficient and (\n            np.any(np.array(tasks_mem_gb) > self.memory_gb)\n            or np.any(np.array(tasks_num_th) > self.processors)\n        ):\n            raise RuntimeError('Insufficient resources available for job')\n\n    def _postrun_check(self):\n        self.pool.shutdown()\n\n    def _check_resources(self, running_tasks):\n        \"\"\"Make sure there are resources available.\"\"\"\n        free_memory_gb = self.memory_gb\n        free_processors = self.processors\n        for _, jobid in running_tasks:\n            free_memory_gb -= min(self.procs[jobid].mem_gb, free_memory_gb)\n            free_processors -= min(self.procs[jobid].n_procs, free_processors)\n\n        return free_memory_gb, free_processors\n\n    def _send_procs_to_workers(self, updatehash=False, graph=None):\n        \"\"\"Submit tasks to workers when system resources are available.\"\"\"\n        import numpy as np\n\n        # Check to see if a job is available (jobs with all dependencies run)\n        # See https://github.com/nipy/nipype/pull/2200#discussion_r141605722\n        # See also https://github.com/nipy/nipype/issues/2372\n        jobids = np.flatnonzero(~self.proc_done & (self.depidx.sum(axis=0) == 0).__array__())\n\n        # Check available resources by summing all threads and memory used\n        free_memory_gb, free_processors = self._check_resources(self.pending_tasks)\n\n        stats = (\n            len(self.pending_tasks),\n            len(jobids),\n            free_memory_gb,\n            self.memory_gb,\n            free_processors,\n            self.processors,\n        )\n        if self._stats != stats:\n            self._stats = stats\n\n        if free_memory_gb < 0.01 or free_processors == 0:\n            return\n\n        if len(jobids) + len(self.pending_tasks) == 0:\n            return\n\n        jobids = self._sort_jobs(jobids, scheduler=self.plugin_args.get('scheduler'))\n\n        # Run garbage collector before potentially submitting jobs\n        gc.collect()\n\n        # Submit jobs\n        for jobid in jobids:\n            # First expand mapnodes\n            if self.procs[jobid].__class__.__name__ == 'MapNode':\n                try:\n                    num_subnodes = self.procs[jobid].num_subnodes()\n                except Exception:\n                    traceback = format_exception(*sys.exc_info())\n                    self._clean_queue(\n                        jobid, graph, result={'result': None, 'traceback': traceback}\n                    )\n                    self.proc_pending[jobid] = False\n                    continue\n                if num_subnodes > 1:\n                    submit = self._submit_mapnode(jobid)\n                    if not submit:\n                        continue\n\n            # Check requirements of this job\n            next_job_gb = min(self.procs[jobid].mem_gb, self.memory_gb)\n            next_job_th = min(self.procs[jobid].n_procs, self.processors)\n\n            # If node does not fit, skip at this moment\n            if next_job_th > free_processors or next_job_gb > free_memory_gb:\n                continue\n\n            free_memory_gb -= next_job_gb\n            free_processors -= next_job_th\n            # change job status in appropriate queues\n            self.proc_done[jobid] = True\n            self.proc_pending[jobid] = True\n\n            # If cached and up-to-date just retrieve it, don't run\n            if self._local_hash_check(jobid, graph):\n                continue\n\n            # updatehash and run_without_submitting are also run locally\n            if updatehash or self.procs[jobid].run_without_submitting:\n                try:\n                    self.procs[jobid].run(updatehash=updatehash)\n                except Exception:\n                    traceback = format_exception(*sys.exc_info())\n                    self._clean_queue(\n                        jobid, graph, result={'result': None, 'traceback': traceback}\n                    )\n\n                # Release resources\n                self._task_finished_cb(jobid)\n                self._remove_node_dirs()\n                free_memory_gb += next_job_gb\n                free_processors += next_job_th\n                # Display stats next loop\n                self._stats = None\n\n                # Clean up any debris from running node in main process\n                gc.collect()\n                continue\n\n            # Task should be submitted to workers\n            # Send job to task manager and add to pending tasks\n            if self._status_callback:\n                self._status_callback(self.procs[jobid], 'start')\n            tid = self._submit_job(deepcopy(self.procs[jobid]), updatehash=updatehash)\n            if tid is None:\n                self.proc_done[jobid] = False\n                self.proc_pending[jobid] = False\n            else:\n                self.pending_tasks.insert(0, (tid, jobid))\n            # Display stats next loop\n            self._stats = None\n\n    def _sort_jobs(self, jobids, scheduler='tsort'):\n        if scheduler == 'mem_thread':\n            return sorted(\n                jobids,\n                key=lambda item: (self.procs[item].mem_gb, self.procs[item].n_procs),\n            )\n        return jobids\n\n\ndef report_crash(node, traceback=None, hostname=None):\n    \"\"\"Writes crash related information to a file\"\"\"\n    name = node._id\n    traceback = traceback or format_exception(*sys.exc_info())\n    timeofcrash = strftime('%Y%m%d-%H%M%S')\n    try:\n        login_name = getpass.getuser()\n    except KeyError:\n        login_name = f'UID{os.getuid():d}'\n\n    crashfile = f'crash-{timeofcrash}-{login_name}-{name}-{str(uuid.uuid4())}'\n    crashdir = node.config['execution'].get('crashdump_dir', os.getcwd())\n\n    os.makedirs(crashdir, exist_ok=True)\n    crashfile = os.path.join(crashdir, crashfile)\n\n    if node.config['execution']['crashfile_format'].lower() in ('text', 'txt', '.txt'):\n        crashfile = f'{crashfile}.txt'\n        crash2txt(crashfile, {'node': node, 'traceback': traceback})\n    else:\n        crashfile = f'{crashfile}.pklz'\n        savepkl(crashfile, {'node': node, 'traceback': traceback}, versioning=True)\n    return crashfile\n"
  },
  {
    "path": "mriqc/instrumentation/__init__.py",
    "content": ""
  },
  {
    "path": "mriqc/instrumentation/__main__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2022 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\nif __name__ == '__main__':\n    import argparse\n\n    from mriqc.instrumentation.resources import FindProcess, ResourceRecorder\n\n    from . import __name__ as module\n\n    # `python -m <module>` typically displays the command as __main__.py\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-n', '--name', type=str, required=True)\n    parser.add_argument('-p', '--logfile_path', type=str, default='.')\n    args = parser.parse_args()\n\n    if '__main__.py' in argparse._sys.argv[0]:  # sys.argv[0]:\n        argparse._sys.argv[0] = f'{argparse._sys.executable} -m {module}'\n\n    pid = args.name if args.name.isnumeric() else FindProcess(args.name)\n\n    ResourceRecorder(pid, log_file=args.logfile_path + str(pid) + '.tsv').run()\n"
  },
  {
    "path": "mriqc/instrumentation/resources.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2022 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Instrumentation to profile resource utilization.\"\"\"\n\nimport signal\nfrom contextlib import suppress\nfrom datetime import datetime, timezone\nfrom multiprocessing import Event, Process\nfrom pathlib import Path\nfrom time import sleep, time_ns\n\nimport psutil\n\nUTC = timezone.utc\n_MB = 1024.0**2\nSAMPLE_ATTRS = (\n    'pid',\n    'name',\n    # \"cmdline\",\n    'cpu_num',\n    'cpu_percent',\n    'memory_info',\n    'num_threads',\n    'num_fds',\n)\n\n\ndef FindProcess(process_name):\n    \"\"\"\n     Find a process by its name and returns its PID. Child processes are excluded\n     Parameters\n     ----------\n     process_name : :obj:`str`\n         The name of the process that must be found.\n    Return\n     ----------\n     PID of the process if found, False if the process is not found\n    \"\"\"\n\n    for proc in psutil.process_iter():\n        try:\n            if process_name == proc.name():\n                parent = proc.parent()\n\n                if parent.name() != process_name:\n                    return proc.pid\n\n        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):\n            pass\n    print('Process ', process_name, ' not found')\n    return False\n\n\ndef sample(\n    pid=None,\n    recursive=True,\n    attrs=SAMPLE_ATTRS,\n    exclude=(),\n):\n    \"\"\"\n    Probe process tree and snapshot current resource utilization.\n    Parameters\n    ----------\n    pid : :obj:`int` or :obj:`None`\n        The process ID that must be sampled. If ``None`` then it samples the\n        current process from which ``sample()`` has been called.\n    recursive : :obj:`bool`\n        Whether the sampler should descend and explore the whole process tree.\n    attrs : :obj:`iterable` of :obj:`str`\n        A list of :obj:`psutil.Process` attribute names that will be retrieved when\n        sampling.\n    \"\"\"\n    proc_list = [psutil.Process(pid)]\n    if proc_list and recursive:\n        with suppress(psutil.NoSuchProcess):\n            proc_list += proc_list[0].children(recursive=True)\n\n    proc_info = []\n    for process in proc_list:\n        if process.pid in exclude:\n            continue\n        with suppress(psutil.NoSuchProcess):\n            proc_info.append(process.as_dict(attrs=attrs))\n\n    return proc_info\n\n\ndef parse_sample(datapoint, timestamp=None, attrs=SAMPLE_ATTRS):\n    \"\"\"Convert a sample dictionary into a list of string values.\"\"\"\n    retval = [f'{timestamp or time_ns()}']\n\n    for attr in attrs:\n        value = datapoint.get(attr, None)\n        if value is None:\n            continue\n\n        if attr == 'cmdline':\n            value = ' '.join(value).replace(\"'\", \"\\\\'\").replace('\"', '\\\\\"')\n            value = [f\"'{value}'\"]\n        elif attr == 'memory_info':\n            value = [f'{value.rss / _MB}', f'{value.vms / _MB}']\n        else:\n            value = [f'{value}']\n\n        retval += value\n\n    return retval\n\n\ndef sample2file(pid=None, recursive=True, timestamp=None, fd=None, flush=True, exclude=()):\n    if fd is None:\n        return\n\n    print(\n        '\\n'.join(\n            [\n                '\\t'.join(parse_sample(s, timestamp=timestamp))\n                for s in sample(pid=pid, recursive=recursive, exclude=exclude)\n            ]\n        ),\n        file=fd,\n    )\n    if flush:\n        fd.flush()\n\n\nclass ResourceRecorder(Process):\n    \"\"\"Attach a ``Thread`` to sample a specific PID with a certain frequency.\"\"\"\n\n    def __init__(self, pid, frequency=0.2, log_file=None, exclude_probe=True, **process_kwargs):\n        Process.__init__(self, name='nipype_resmon', daemon=True, **process_kwargs)\n\n        self._pid = int(pid)\n        \"\"\"The process to be sampled.\"\"\"\n        self._logfile = str(\n            Path(log_file if log_file is not None else f'.prof-{pid}.tsv').absolute()\n        )\n        \"\"\"An open file descriptor where results are dumped.\"\"\"\n        self._exclude = exclude_probe or ()\n        \"\"\"A list/tuple containing PIDs that should not be monitored.\"\"\"\n        self._freq_ns = int(max(frequency, 0.02) * 1e9)\n        \"\"\"Sampling frequency (stored in ns).\"\"\"\n        self._done = Event()\n        \"\"\"Flag indicating if the process is marked to finish.\"\"\"\n\n        signal.signal(signal.SIGINT, self.stop)\n        signal.signal(signal.SIGTERM, self.stop)\n\n    def run(self, *args, **kwargs):\n        \"\"\"Core monitoring function, called by start()\"\"\"\n        # Open file now, because it cannot be pickled.\n\n        Path(self._logfile).parent.mkdir(parents=True, exist_ok=True)\n        _logfile = Path(self._logfile).open('w')\n\n        # Write headers (comment trace + header row)\n        _header = [\n            f'# MRIQC Resource recorder started tracking PID {self._pid} '\n            f'{datetime.now(tz=UTC).strftime(\"(%Y/%m/%d; %H:%M:%S)\")}',\n            '\\t'.join(('timestamp', *SAMPLE_ATTRS)).replace(\n                'memory_info', 'mem_rss_mb\\tmem_vsm_mb'\n            ),\n        ]\n        print('\\n'.join(_header), file=_logfile)\n\n        # Add self to exclude list if pertinent\n        if self._exclude is True:\n            self._exclude = (psutil.Process().pid,)\n\n        # Ensure done is not marked set\n        self._done.clear()\n\n        # Initiate periodic sampling\n        start_time = time_ns()\n        wait_til = start_time\n        while not self._done.is_set():\n            try:\n                sample2file(self._pid, fd=_logfile, timestamp=wait_til)\n            except psutil.NoSuchProcess:\n                print(\n                    f'# MRIQC Resource recorder finished '\n                    f'{datetime.now(tz=UTC).strftime(\"(%Y/%m/%d; %H:%M:%S)\")}',\n                    file=_logfile,\n                )\n                _logfile.flush()\n                _logfile.close()\n                break\n\n            wait_til += self._freq_ns\n            sleep(max(0, (wait_til - time_ns()) / 1.0e9))\n\n        _logfile.close()\n\n    def stop(self, *args):\n        # Tear-down process\n        self._done.set()\n        with Path(self._logfile).open('a') as f:\n            f.write(\n                f'# MRIQC Resource recorder finished '\n                f'{datetime.now(tz=UTC).strftime(\"(%Y/%m/%d; %H:%M:%S)\")}',\n            )\n"
  },
  {
    "path": "mriqc/instrumentation/viz.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2022 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Visualizing resource recordings.\"\"\"\n\nimport numpy as np\nimport pandas as pd\nfrom matplotlib import pyplot as plt\n\n_TIME_LABEL = 'runtime'\n\n\ndef plot(filename, param='mem_vsm_mb', mask_processes=(), out_file=None):\n    \"\"\"Plot a recording file.\"\"\"\n    data = pd.read_csv(filename, sep=r'\\s+', comment='#')\n\n    # Rebase all events to be relative to start and convert to seconds\n    data['timestamp'] -= data['timestamp'][0]\n    data['timestamp'] /= 1.0e9\n\n    # Convert processes from rows to columns\n    unified = pd.DataFrame({_TIME_LABEL: sorted(set(data['timestamp'].values))})\n\n    pids = sorted(set(data['pid'].values))\n    proc_names = []\n    for pid in pids:\n        pid_info = data[data['pid'] == pid]\n        try:\n            label = f'{pid_info[\"name\"].values[0]}'\n        except KeyError:\n            label = f'{pid}'\n\n        if label in mask_processes:\n            continue\n\n        rows = unified[_TIME_LABEL].isin(pid_info['timestamp'])\n        if label not in unified.columns:\n            proc_names.append(label)\n            unified[label] = 0\n\n        unified.loc[rows, label] += pid_info[param].values / (\n            1024 if param.startswith('mem') else 1\n        )\n\n    unified[proc_names] = unified[proc_names].replace({0: np.nan})\n\n    fig = plt.figure(figsize=(15, 10), facecolor='white')\n    ax = plt.gca()\n    _ = unified.plot.area(x=_TIME_LABEL, cmap='tab20', linewidth=0, ax=ax)\n    ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n    ax.set_xlabel('Run time (mm:ss)')\n    if param.startswith('mem'):\n        ax.set_ylabel('Memory fingerprint (GB)')\n    elif param.startswith('cpu'):\n        ax.set_ylabel('CPU utilization')\n    else:\n        ax.set_ylabel(param)\n\n    ax.set_xticklabels([f'{int(float(v) // 60)}:{int(float(v) % 60)}' for v in ax.get_xticks()])\n    if out_file is not None:\n        fig.savefig(out_file, bbox_inches='tight', pad_inches=0, dpi=300)\n        return\n\n    return fig\n"
  },
  {
    "path": "mriqc/interfaces/__init__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"mriqc nipype interfaces\"\"\"\n\nfrom niworkflows.interfaces.bids import DerivativesDataSink as _DDSink\n\nfrom mriqc.interfaces.anatomical import (\n    ArtifactMask,\n    ComputeQI2,\n    Harmonize,\n    RotationMask,\n    StructuralQC,\n)\nfrom mriqc.interfaces.bids import IQMFileSink\nfrom mriqc.interfaces.common import ConformImage, EnsureSize\nfrom mriqc.interfaces.functional import FunctionalQC, GatherTimeseries, Spikes\nfrom mriqc.interfaces.webapi import UploadIQMs\n\n\nclass DerivativesDataSink(_DDSink):\n    out_path_base = ''\n\n\n__all__ = [\n    'ArtifactMask',\n    'ComputeQI2',\n    'ConformImage',\n    'DerivativesDataSink',\n    'EnsureSize',\n    'FunctionalQC',\n    'GatherTimeseries',\n    'Harmonize',\n    'IQMFileSink',\n    'RotationMask',\n    'Spikes',\n    'StructuralQC',\n    'UploadIQMs',\n]\n"
  },
  {
    "path": "mriqc/interfaces/anatomical.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Nipype interfaces to support anatomical workflow.\"\"\"\n\nfrom pathlib import Path\n\nimport nibabel as nb\nimport numpy as np\nimport scipy.ndimage as nd\nfrom nipype.interfaces.base import (\n    BaseInterfaceInputSpec,\n    File,\n    InputMultiPath,\n    SimpleInterface,\n    TraitedSpec,\n    traits,\n)\nfrom nipype.utils.filemanip import fname_presuffix\n\nfrom mriqc.qc.anatomical import (\n    art_qi1,\n    art_qi2,\n    cjv,\n    cnr,\n    efc,\n    fber,\n    rpve,\n    snr,\n    snr_dietrich,\n    summary_stats,\n    volume_fraction,\n    wm2max,\n)\nfrom mriqc.utils.misc import _flatten_dict\n\n\nclass StructuralQCInputSpec(BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='file to be plotted')\n    in_noinu = File(exists=True, mandatory=True, desc='image after INU correction')\n    in_segm = File(exists=True, mandatory=True, desc='segmentation file from FSL FAST')\n    in_bias = File(exists=True, mandatory=True, desc='bias file')\n    head_msk = File(exists=True, mandatory=True, desc='head mask')\n    air_msk = File(exists=True, mandatory=True, desc='air mask')\n    rot_msk = File(exists=True, mandatory=True, desc='rotation mask')\n    artifact_msk = File(exists=True, mandatory=True, desc='air mask')\n    in_pvms = InputMultiPath(\n        File(exists=True),\n        mandatory=True,\n        desc='partial volume maps from FSL FAST',\n    )\n    in_tpms = InputMultiPath(File(), desc='tissue probability maps from FSL FAST')\n    mni_tpms = InputMultiPath(File(), desc='tissue probability maps from FSL FAST')\n    in_fwhm = traits.List(traits.Float, mandatory=True, desc='smoothness estimated with AFNI')\n    human = traits.Bool(True, usedefault=True, desc='human workflow')\n\n\nclass StructuralQCOutputSpec(TraitedSpec):\n    summary = traits.Dict(desc='summary statistics per tissue')\n    icvs = traits.Dict(desc='intracranial volume (ICV) fractions')\n    rpve = traits.Dict(desc='partial volume fractions')\n    size = traits.Dict(desc='image sizes')\n    spacing = traits.Dict(desc='image sizes')\n    fwhm = traits.Dict(desc='full width half-maximum measure')\n    inu = traits.Dict(desc='summary statistics of the bias field')\n    snr = traits.Dict\n    snrd = traits.Dict\n    cnr = traits.Float\n    fber = traits.Float\n    efc = traits.Float\n    qi_1 = traits.Float\n    wm2max = traits.Float\n    cjv = traits.Float\n    out_qc = traits.Dict(desc='output flattened dictionary with all measures')\n    out_noisefit = File(exists=True, desc='plot of background noise and chi fitting')\n    tpm_overlap = traits.Dict\n\n\nclass StructuralQC(SimpleInterface):\n    \"\"\"\n    Computes anatomical :abbr:`QC (Quality Control)` measures on the\n    structural image given as input\n\n    \"\"\"\n\n    input_spec = StructuralQCInputSpec\n    output_spec = StructuralQCOutputSpec\n\n    def _run_interface(self, runtime):  # pylint: disable=R0914,E1101\n        imnii = nb.load(self.inputs.in_noinu)\n\n        # Load image corrected for INU\n        inudata = np.nan_to_num(imnii.get_fdata())\n        inudata[inudata < 0] = 0\n\n        if np.all(inudata < 1e-5):\n            raise RuntimeError(\n                'Input inhomogeneity-corrected data seem empty. '\n                'MRIQC failed to process this dataset.'\n            )\n\n        # Load binary segmentation from FSL FAST\n        segnii = nb.load(self.inputs.in_segm)\n        segdata = np.asanyarray(segnii.dataobj).astype(np.uint8)\n\n        if np.sum(segdata > 0) < 1e3:\n            raise RuntimeError(\n                'Input segmentation data is likely corrupt. MRIQC failed to process this dataset.'\n            )\n\n        # Load air, artifacts and head masks\n        airdata = np.asanyarray(nb.load(self.inputs.air_msk).dataobj).astype(np.uint8)\n        artdata = np.asanyarray(nb.load(self.inputs.artifact_msk).dataobj).astype(np.uint8)\n\n        headdata = np.asanyarray(nb.load(self.inputs.head_msk).dataobj).astype(np.uint8)\n        if np.sum(headdata > 0) < 100:\n            raise RuntimeError(\n                'Detected less than 100 voxels belonging to the head mask. '\n                'MRIQC failed to process this dataset.'\n            )\n\n        rotdata = np.asanyarray(nb.load(self.inputs.rot_msk).dataobj).astype(np.uint8)\n\n        # Load brain tissue probability maps from GMM segmentation\n        pvms = {\n            label: nb.load(fname).get_fdata()\n            for label, fname in zip(('csf', 'gm', 'wm'), self.inputs.in_pvms)\n        }\n        pvmdata = list(pvms.values())\n\n        # Add probability maps\n        pvms['bg'] = airdata\n\n        # Summary stats\n        stats = summary_stats(inudata, pvms)\n        self._results['summary'] = stats\n\n        # SNR\n        snrvals = []\n        self._results['snr'] = {}\n        for tlabel in ('csf', 'wm', 'gm'):\n            snrvals.append(\n                snr(\n                    stats[tlabel]['median'],\n                    stats[tlabel]['stdv'],\n                    stats[tlabel]['n'],\n                )\n            )\n            self._results['snr'][tlabel] = snrvals[-1]\n        self._results['snr']['total'] = float(np.mean(snrvals))\n\n        snrvals = []\n        self._results['snrd'] = {\n            tlabel: snr_dietrich(\n                stats[tlabel]['median'],\n                mad_air=stats['bg']['mad'],\n                sigma_air=stats['bg']['stdv'],\n            )\n            for tlabel in ['csf', 'wm', 'gm']\n        }\n        self._results['snrd']['total'] = float(\n            np.mean([val for _, val in list(self._results['snrd'].items())])\n        )\n\n        # CNR\n        self._results['cnr'] = cnr(\n            stats['wm']['median'],\n            stats['gm']['median'],\n            stats['bg']['stdv'],\n            stats['wm']['stdv'],\n            stats['gm']['stdv'],\n        )\n\n        # FBER\n        self._results['fber'] = fber(inudata, headdata, rotdata)\n\n        # EFC\n        self._results['efc'] = efc(inudata, rotdata)\n\n        # M2WM\n        self._results['wm2max'] = wm2max(inudata, stats['wm']['median'])\n\n        # Artifacts\n        self._results['qi_1'] = art_qi1(airdata, artdata)\n\n        # CJV\n        self._results['cjv'] = cjv(\n            # mu_wm, mu_gm, sigma_wm, sigma_gm\n            stats['wm']['median'],\n            stats['gm']['median'],\n            stats['wm']['mad'],\n            stats['gm']['mad'],\n        )\n\n        # FWHM\n        fwhm = np.array(self.inputs.in_fwhm[:3]) / np.array(imnii.header.get_zooms()[:3])\n        self._results['fwhm'] = {\n            'x': float(fwhm[0]),\n            'y': float(fwhm[1]),\n            'z': float(fwhm[2]),\n            'avg': float(np.average(fwhm)),\n        }\n\n        # ICVs\n        self._results['icvs'] = volume_fraction(pvmdata)\n\n        # RPVE\n        self._results['rpve'] = rpve(pvmdata, segdata)\n\n        # Image specs\n        self._results['size'] = {\n            'x': int(inudata.shape[0]),\n            'y': int(inudata.shape[1]),\n            'z': int(inudata.shape[2]),\n        }\n        self._results['spacing'] = {\n            i: float(v) for i, v in zip(['x', 'y', 'z'], imnii.header.get_zooms()[:3])\n        }\n\n        try:\n            self._results['size']['t'] = int(inudata.shape[3])\n        except IndexError:\n            pass\n\n        try:\n            self._results['spacing']['tr'] = float(imnii.header.get_zooms()[3])\n        except IndexError:\n            pass\n\n        # Bias\n        bias = nb.load(self.inputs.in_bias).get_fdata()[segdata > 0]\n        self._results['inu'] = {\n            'range': float(np.abs(np.percentile(bias, 95.0) - np.percentile(bias, 5.0))),\n            'med': float(np.median(bias)),\n        }  # pylint: disable=E1101\n\n        mni_tpms = [nb.load(tpm).get_fdata() for tpm in self.inputs.mni_tpms]\n        in_tpms = [nb.load(tpm).get_fdata() for tpm in self.inputs.in_pvms]\n        overlap = fuzzy_jaccard(in_tpms, mni_tpms)\n        self._results['tpm_overlap'] = {\n            'csf': overlap[0],\n            'gm': overlap[1],\n            'wm': overlap[2],\n        }\n\n        # Flatten the dictionary\n        self._results['out_qc'] = _flatten_dict(self._results)\n        return runtime\n\n\nclass _ArtifactMaskInputSpec(BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='File to be plotted')\n    head_mask = File(exists=True, mandatory=True, desc='head mask')\n    glabella_xyz = traits.Tuple(\n        (0.0, 90.0, -14.0),\n        types=(traits.Float, traits.Float, traits.Float),\n        usedefault=True,\n        desc='position of the top of the glabella in standard coordinates',\n    )\n    inion_xyz = traits.Tuple(\n        (0.0, -120.0, -14.0),\n        types=(traits.Float, traits.Float, traits.Float),\n        usedefault=True,\n        desc='position of the top of the inion in standard coordinates',\n    )\n    ind2std_xfm = File(exists=True, mandatory=True, desc='individual to standard affine transform')\n    zscore = traits.Float(10.0, usedefault=True, desc='z-score to consider artifacts')\n\n\nclass _ArtifactMaskOutputSpec(TraitedSpec):\n    out_hat_msk = File(exists=True, desc='output \"hat\" mask')\n    out_art_msk = File(exists=True, desc='output artifacts mask')\n    out_air_msk = File(exists=True, desc='output \"hat\" mask, without artifacts')\n\n\nclass ArtifactMask(SimpleInterface):\n    \"\"\"\n    Computes the artifact mask using the method described in [Mortamet2009]_.\n    \"\"\"\n\n    input_spec = _ArtifactMaskInputSpec\n    output_spec = _ArtifactMaskOutputSpec\n\n    def _run_interface(self, runtime):\n        from nibabel.affines import apply_affine\n        from nitransforms.linear import Affine\n\n        in_file = Path(self.inputs.in_file)\n        imnii = nb.as_closest_canonical(nb.load(in_file))\n        imdata = np.nan_to_num(imnii.get_fdata().astype(np.float32))\n\n        xfm = Affine.from_filename(self.inputs.ind2std_xfm, fmt='itk')\n\n        ras2ijk = np.linalg.inv(imnii.affine)\n        glabella_ijk, inion_ijk = apply_affine(\n            ras2ijk, xfm.map([self.inputs.glabella_xyz, self.inputs.inion_xyz])\n        )\n\n        hmdata = np.bool_(nb.load(self.inputs.head_mask).dataobj)\n\n        # Calculate distance to border\n        dist = nd.morphology.distance_transform_edt(~hmdata)\n\n        hmdata[:, :, : int(inion_ijk[2])] = 1\n        hmdata[:, (hmdata.shape[1] // 2) :, : int(glabella_ijk[2])] = 1\n\n        dist[~hmdata] = 0\n        dist /= dist.max()\n\n        # Run the artifact detection\n        qi1_img = artifact_mask(imdata, (~hmdata), dist, zscore=self.inputs.zscore)\n\n        fname = in_file.relative_to(in_file.parent).stem\n        ext = ''.join(in_file.suffixes)\n\n        outdir = Path(runtime.cwd).absolute()\n        self._results['out_hat_msk'] = str(outdir / f'{fname}_hat{ext}')\n        self._results['out_art_msk'] = str(outdir / f'{fname}_art{ext}')\n        self._results['out_air_msk'] = str(outdir / f'{fname}_air{ext}')\n\n        hdr = imnii.header.copy()\n        hdr.set_data_dtype(np.uint8)\n        imnii.__class__(qi1_img.astype(np.uint8), imnii.affine, hdr).to_filename(\n            self._results['out_art_msk']\n        )\n\n        airdata = (~hmdata).astype(np.uint8)\n        imnii.__class__(airdata, imnii.affine, hdr).to_filename(self._results['out_hat_msk'])\n\n        airdata[qi1_img > 0] = 0\n        imnii.__class__(airdata.astype(np.uint8), imnii.affine, hdr).to_filename(\n            self._results['out_air_msk']\n        )\n        return runtime\n\n\nclass ComputeQI2InputSpec(BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='File to be plotted')\n    air_msk = File(exists=True, mandatory=True, desc='air (without artifacts) mask')\n\n\nclass ComputeQI2OutputSpec(TraitedSpec):\n    qi2 = traits.Float(desc='computed QI2 value')\n    out_file = File(desc='output plot: noise fit')\n\n\nclass ComputeQI2(SimpleInterface):\n    \"\"\"\n    Computes the artifact mask using the method described in [Mortamet2009]_.\n    \"\"\"\n\n    input_spec = ComputeQI2InputSpec\n    output_spec = ComputeQI2OutputSpec\n\n    def _run_interface(self, runtime):\n        imdata = nb.load(self.inputs.in_file).get_fdata()\n        airdata = nb.load(self.inputs.air_msk).get_fdata()\n        qi2, out_file = art_qi2(imdata, airdata)\n        self._results['qi2'] = qi2\n        self._results['out_file'] = out_file\n        return runtime\n\n\nclass HarmonizeInputSpec(BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='input data (after bias correction)')\n    wm_mask = File(exists=True, mandatory=True, desc='white-matter mask')\n    brain_mask = File(exists=True, desc='brain mask (fall-back in case [near]-empty WM mask)')\n    erodemsk = traits.Bool(True, usedefault=True, desc='erode mask')\n    thresh = traits.Float(0.9, usedefault=True, desc='WM probability threshold')\n    min_size = traits.Int(30, usedefault=True, desc='minimum number of voxels in binary WM mask')\n\n\nclass HarmonizeOutputSpec(TraitedSpec):\n    out_file = File(exists=True, desc='input data (after intensity harmonization)')\n\n\nclass Harmonize(SimpleInterface):\n    \"\"\"\n    Computes the artifact mask using the method described in [Mortamet2009]_.\n    \"\"\"\n\n    input_spec = HarmonizeInputSpec\n    output_spec = HarmonizeOutputSpec\n\n    def _run_interface(self, runtime):\n        in_file = nb.load(self.inputs.in_file)\n        data = in_file.get_fdata()\n\n        wm_mask = nb.load(self.inputs.wm_mask).get_fdata()\n        wm_mask[wm_mask < self.inputs.thresh] = 0\n        wm_mask[wm_mask > 0] = 1\n        wm_mask = wm_mask.astype(bool)\n        wm_mask_size = wm_mask.sum()\n\n        if wm_mask_size < self.inputs.min_size:\n            brain_mask = nb.load(self.inputs.brain_mask).get_fdata() > 0.5\n            wm_mask = brain_mask.copy()\n            wm_mask[data < np.percentile(data[brain_mask], 75)] = False\n            wm_mask[data > np.percentile(data[brain_mask], 95)] = False\n        elif self.inputs.erodemsk:\n            # Create a structural element to be used in an opening operation.\n            struct = nd.generate_binary_structure(3, 2)\n            # Perform an opening operation on the background data.\n            wm_mask = nd.binary_erosion(wm_mask.astype(np.uint8), structure=struct).astype(bool)\n\n        data *= 1000.0 / np.median(data[wm_mask])\n\n        out_file = fname_presuffix(self.inputs.in_file, suffix='_harmonized', newpath='.')\n        in_file.__class__(data, in_file.affine, in_file.header).to_filename(out_file)\n\n        self._results['out_file'] = out_file\n\n        return runtime\n\n\nclass RotationMaskInputSpec(BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='input data')\n\n\nclass RotationMaskOutputSpec(TraitedSpec):\n    out_file = File(exists=True, desc='rotation mask (if any)')\n\n\nclass RotationMask(SimpleInterface):\n    \"\"\"\n    Computes the artifact mask using the method described in [Mortamet2009]_.\n    \"\"\"\n\n    input_spec = RotationMaskInputSpec\n    output_spec = RotationMaskOutputSpec\n\n    def _run_interface(self, runtime):\n        in_file = nb.load(self.inputs.in_file)\n        data = in_file.get_fdata()\n        mask = data <= 0\n\n        # Pad one pixel to control behavior on borders of binary_opening\n        mask = np.pad(mask, pad_width=(1,), mode='constant', constant_values=1)\n\n        # Remove noise\n        struct = nd.generate_binary_structure(3, 2)\n        mask = nd.binary_opening(mask, structure=struct).astype(np.uint8)\n\n        # Remove small objects\n        label_im, nb_labels = nd.label(mask)\n        if nb_labels > 2:\n            sizes = nd.sum(mask, label_im, list(range(nb_labels + 1)))\n            ordered = sorted(zip(sizes, list(range(nb_labels + 1))), reverse=True)\n            for _, label in ordered[2:]:\n                mask[label_im == label] = 0\n\n        # Un-pad\n        mask = mask[1:-1, 1:-1, 1:-1]\n\n        # If mask is small, clean-up\n        if mask.sum() < 500:\n            mask = np.zeros_like(mask, dtype=np.uint8)\n\n        out_img = in_file.__class__(mask, in_file.affine, in_file.header)\n        out_img.header.set_data_dtype(np.uint8)\n\n        out_file = fname_presuffix(self.inputs.in_file, suffix='_rotmask', newpath='.')\n        out_img.to_filename(out_file)\n        self._results['out_file'] = out_file\n        return runtime\n\n\ndef artifact_mask(imdata, airdata, distance, zscore=10.0):\n    \"\"\"Compute a mask of artifacts found in the air region.\"\"\"\n    from statsmodels.robust.scale import mad\n\n    qi1_msk = np.zeros(imdata.shape, dtype=bool)\n    bg_data = imdata[airdata]\n    if (bg_data > 0).sum() < 10:\n        return qi1_msk\n\n    # Standardize the distribution of the background\n    bg_spread = mad(bg_data[bg_data > 0])\n    bg_data[bg_data > 0] = bg_data[bg_data > 0] / bg_spread\n\n    # Apply this threshold to the background voxels to identify voxels\n    # contributing artifacts.\n    qi1_msk[airdata] = bg_data > zscore\n    qi1_msk[distance < 0.10] = False\n\n    # Create a structural element to be used in an opening operation.\n    struct = nd.generate_binary_structure(3, 1)\n    qi1_msk = nd.binary_opening(qi1_msk, struct).astype(np.uint8)\n    return qi1_msk\n\n\ndef fuzzy_jaccard(in_tpms, in_mni_tpms):\n    overlaps = []\n    for tpm, mni_tpm in zip(in_tpms, in_mni_tpms):\n        tpm = tpm.reshape(-1)\n        mni_tpm = mni_tpm.reshape(-1)\n\n        num = np.min([tpm, mni_tpm], axis=0).sum()\n        den = np.max([tpm, mni_tpm], axis=0).sum()\n        overlaps.append(float(num / den))\n    return overlaps\n"
  },
  {
    "path": "mriqc/interfaces/bids.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nimport re\nfrom pathlib import Path\n\nimport orjson as json\nfrom nipype.interfaces.base import (\n    BaseInterfaceInputSpec,\n    DynamicTraitedSpec,\n    File,\n    SimpleInterface,\n    Str,\n    TraitedSpec,\n    Undefined,\n    isdefined,\n    traits,\n)\n\nfrom mriqc import config\nfrom mriqc.utils.misc import BIDS_COMP\n\n\nclass IQMFileSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec):\n    in_file = Str(mandatory=True, desc='path of input file')\n    modality = Str(mandatory=True, desc='the qc type')\n    entities = traits.Dict(desc='entities corresponding to the input')\n    subject_id = Str(desc='the subject id')\n    session_id = traits.Either(None, Str, usedefault=True)\n    task_id = traits.Either(None, Str, usedefault=True)\n    acq_id = traits.Either(None, Str, usedefault=True)\n    rec_id = traits.Either(None, Str, usedefault=True)\n    run_id = traits.Either(None, traits.Int, usedefault=True)\n    dataset = Str(desc='dataset identifier')\n    dismiss_entities = traits.List(\n        ['datatype', 'part', 'echo', 'extension', 'suffix'],\n        usedefault=True,\n    )\n    metadata = traits.Dict()\n    provenance = traits.Dict()\n\n    root = traits.Dict(desc='output root dictionary')\n    out_dir = File(desc='the output directory')\n    _outputs = traits.Dict(value={}, usedefault=True)\n\n    def __setattr__(self, key, value):\n        if key not in self.copyable_trait_names():\n            if not isdefined(value):\n                super().__setattr__(key, value)\n            self._outputs[key] = value\n        else:\n            if key in self._outputs:\n                self._outputs[key] = value\n            super().__setattr__(key, value)\n\n\nclass IQMFileSinkOutputSpec(TraitedSpec):\n    out_file = File(desc='the output JSON file containing the IQMs')\n\n\nclass IQMFileSink(SimpleInterface):\n    input_spec = IQMFileSinkInputSpec\n    output_spec = IQMFileSinkOutputSpec\n    expr = re.compile('^root[0-9]+$')\n\n    def __init__(self, fields=None, force_run=True, **inputs):\n        super().__init__(**inputs)\n\n        if fields is None:\n            fields = []\n\n        self._out_dict = {}\n\n        # Initialize fields\n        fields = list(set(fields) - set(self.inputs.copyable_trait_names()))\n        self._input_names = fields\n        undefined_traits = {key: self._add_field(key) for key in fields}\n        self.inputs.trait_set(trait_change_notify=False, **undefined_traits)\n\n        if force_run:\n            self._always_run = True\n\n    def _add_field(self, name, value=Undefined):\n        self.inputs.add_trait(name, traits.Any)\n        self.inputs._outputs[name] = value\n        return value\n\n    def _gen_outfile(self):\n        out_dir = Path()\n        if isdefined(self.inputs.out_dir):\n            out_dir = Path(self.inputs.out_dir)\n\n        # Crawl back to the BIDS root\n        path = Path(self.inputs.in_file)\n        for i in range(1, 4):\n            if str(path.parents[i].name).startswith('sub-'):\n                bids_root = path.parents[i + 1]\n                break\n        in_file = str(path.relative_to(bids_root))\n\n        if isdefined(self.inputs.dismiss_entities) and (dismiss := self.inputs.dismiss_entities):\n            for entity in dismiss:\n                bids_chunks = [\n                    chunk for chunk in path.name.split('_') if not chunk.startswith(f'{entity}-')\n                ]\n                path = path.parent / '_'.join(bids_chunks)\n\n        # Build path and ensure directory exists\n        bids_path = out_dir / in_file.replace(''.join(Path(in_file).suffixes), '.json')\n        bids_path.parent.mkdir(parents=True, exist_ok=True)\n        self._results['out_file'] = str(bids_path)\n        return self._results['out_file']\n\n    def _run_interface(self, runtime):\n        out_file = self._gen_outfile()\n\n        if isdefined(self.inputs.root):\n            self._out_dict = self.inputs.root\n\n        root_adds = []\n        for key, val in list(self.inputs._outputs.items()):\n            if not isdefined(val) or key == 'trait_added':\n                continue\n\n            if self.expr.match(key) is not None:\n                root_adds.append(key)\n                continue\n\n            key, val = _process_name(key, val)\n            self._out_dict[key] = val\n\n        for root_key in root_adds:\n            val = self.inputs._outputs.get(root_key, None)\n            if isinstance(val, dict):\n                self._out_dict.update(val)\n            else:\n                config.loggers.interface.warning(\n                    'Output \"%s\" is not a dictionary (value=\"%s\"), discarding output.',\n                    root_key,\n                    str(val),\n                )\n\n        # Fill in the \"bids_meta\" key\n        id_dict = self.inputs.entities if isdefined(self.inputs.entities) else {}\n        for comp in BIDS_COMP:\n            comp_val = getattr(self.inputs, comp, None)\n            if isdefined(comp_val) and comp_val is not None:\n                id_dict[comp] = comp_val\n        id_dict['modality'] = self.inputs.modality\n\n        if isdefined(self.inputs.metadata) and self.inputs.metadata:\n            id_dict.update(self.inputs.metadata)\n\n        if self._out_dict.get('bids_meta') is None:\n            self._out_dict['bids_meta'] = {}\n        self._out_dict['bids_meta'].update(id_dict)\n\n        if isdefined(self.inputs.dataset):\n            self._out_dict['bids_meta']['dataset'] = self.inputs.dataset\n\n        # Fill in the \"provenance\" key\n        # Predict QA from IQMs and add to metadata\n        prov_dict = {}\n        if isdefined(self.inputs.provenance) and self.inputs.provenance:\n            prov_dict.update(self.inputs.provenance)\n\n        if self._out_dict.get('provenance') is None:\n            self._out_dict['provenance'] = {}\n        self._out_dict['provenance'].update(prov_dict)\n\n        Path(out_file).write_bytes(\n            json.dumps(\n                self._out_dict,\n                option=(\n                    json.OPT_SORT_KEYS\n                    | json.OPT_INDENT_2\n                    | json.OPT_APPEND_NEWLINE\n                    | json.OPT_SERIALIZE_NUMPY\n                ),\n            )\n        )\n        return runtime\n\n\ndef _process_name(name, val):\n    if '.' in name:\n        newkeys = name.split('.')\n        name = newkeys.pop(0)\n        nested_dict = {newkeys.pop(): val}\n\n        for nk in reversed(newkeys):\n            nested_dict = {nk: nested_dict}\n        val = nested_dict\n\n    return name, val\n"
  },
  {
    "path": "mriqc/interfaces/common/__init__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nfrom mriqc.interfaces.common.conform_image import ConformImage\nfrom mriqc.interfaces.common.ensure_size import EnsureSize\n\n__all__ = (\n    'ConformImage',\n    'EnsureSize',\n)\n"
  },
  {
    "path": "mriqc/interfaces/common/conform_image.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nDefinition of the :class:`ConformImage` interface.\n\"\"\"\n\nfrom os import path as op\n\nimport nibabel as nib\nimport numpy as np\nfrom nipype.interfaces.base import (\n    BaseInterfaceInputSpec,\n    File,\n    SimpleInterface,\n    TraitedSpec,\n    traits,\n)\n\nfrom mriqc import config, messages\n\n#: Output file name format.\nOUT_FILE = '{prefix}_conformed{ext}'\n\n#: NIfTI header datatype code to numpy dtype.\nNUMPY_DTYPE = {\n    1: np.uint8,\n    2: np.uint8,\n    4: np.uint16,\n    8: np.uint32,\n    64: np.float32,\n    256: np.uint8,\n    1024: np.uint32,\n    1280: np.uint32,\n    1536: np.float32,\n}\n\n\nclass ConformImageInputSpec(BaseInterfaceInputSpec):\n    \"\"\"\n    Input specification for the :class:`ConformImage` interface.\n    \"\"\"\n\n    in_file = File(exists=True, mandatory=True, desc='input image')\n    check_ras = traits.Bool(True, usedefault=True, desc='check that orientation is RAS')\n    check_dtype = traits.Bool(True, usedefault=True, desc='check data type')\n\n\nclass ConformImageOutputSpec(TraitedSpec):\n    \"\"\"\n    Output specification for the :class:`ConformImage` interface.\n    \"\"\"\n\n    out_file = File(exists=True, desc='output conformed file')\n\n\nclass ConformImage(SimpleInterface):\n    \"\"\"\n    Conforms an input image.\n\n    List of nifti datatypes:\n\n    .. note: Original Analyze 7.5 types\n\n          DT_NONE                    0\n          DT_UNKNOWN                 0     / what it says, dude           /\n          DT_BINARY                  1     / binary (1 bit/voxel)         /\n          DT_UNSIGNED_CHAR           2     / unsigned char (8 bits/voxel) /\n          DT_SIGNED_SHORT            4     / signed short (16 bits/voxel) /\n          DT_SIGNED_INT              8     / signed int (32 bits/voxel)   /\n          DT_FLOAT                  16     / float (32 bits/voxel)        /\n          DT_COMPLEX                32     / complex (64 bits/voxel)      /\n          DT_DOUBLE                 64     / double (64 bits/voxel)       /\n          DT_RGB                   128     / RGB triple (24 bits/voxel)   /\n          DT_ALL                   255     / not very useful (?)          /\n\n    .. note: Added names for the same data types\n\n          DT_UINT8                   2\n          DT_INT16                   4\n          DT_INT32                   8\n          DT_FLOAT32                16\n          DT_COMPLEX64              32\n          DT_FLOAT64                64\n          DT_RGB24                 128\n\n    .. note: New codes for NIfTI\n\n          DT_INT8                  256     / signed char (8 bits)         /\n          DT_UINT16                512     / unsigned short (16 bits)     /\n          DT_UINT32                768     / unsigned int (32 bits)       /\n          DT_INT64                1024     / long long (64 bits)          /\n          DT_UINT64               1280     / unsigned long long (64 bits) /\n          DT_FLOAT128             1536     / long double (128 bits)       /\n          DT_COMPLEX128           1792     / double pair (128 bits)       /\n          DT_COMPLEX256           2048     / long double pair (256 bits)  /\n          NIFTI_TYPE_UINT8           2 /! unsigned char. /\n          NIFTI_TYPE_INT16           4 /! signed short. /\n          NIFTI_TYPE_INT32           8 /! signed int. /\n          NIFTI_TYPE_FLOAT32        16 /! 32 bit float. /\n          NIFTI_TYPE_COMPLEX64      32 /! 64 bit complex = 2 32 bit floats. /\n          NIFTI_TYPE_FLOAT64        64 /! 64 bit float = double. /\n          NIFTI_TYPE_RGB24         128 /! 3 8 bit bytes. /\n          NIFTI_TYPE_INT8          256 /! signed char. /\n          NIFTI_TYPE_UINT16        512 /! unsigned short. /\n          NIFTI_TYPE_UINT32        768 /! unsigned int. /\n          NIFTI_TYPE_INT64        1024 /! signed long long. /\n          NIFTI_TYPE_UINT64       1280 /! unsigned long long. /\n          NIFTI_TYPE_FLOAT128     1536 /! 128 bit float = long double. /\n          NIFTI_TYPE_COMPLEX128   1792 /! 128 bit complex = 2 64 bit floats. /\n          NIFTI_TYPE_COMPLEX256   2048 /! 256 bit complex = 2 128 bit floats /\n\n    \"\"\"\n\n    input_spec = ConformImageInputSpec\n    output_spec = ConformImageOutputSpec\n\n    def _warn_suspicious_dtype(self, dtype: int) -> None:\n        \"\"\"\n        Warns about binary type *nii* images.\n\n        Parameters\n        ----------\n        dtype : int\n            NIfTI header datatype\n        \"\"\"\n        if dtype == 1:\n            dtype_message = messages.SUSPICIOUS_DATA_TYPE.format(\n                in_file=self.inputs.in_file, dtype=dtype\n            )\n            config.loggers.interface.warning(dtype_message)\n\n    def _check_dtype(self, nii: nib.Nifti1Image) -> nib.Nifti1Image:\n        \"\"\"\n        Checks the NIfTI header datatype and converts the data to the matching\n        numpy dtype.\n\n        Parameters\n        ----------\n        nii : nib.Nifti1Image\n            Input image\n\n        Returns\n        -------\n        nib.Nifti1Image\n            Converted input image\n        \"\"\"\n        header = nii.header.copy()\n        datatype = int(header['datatype'])\n        self._warn_suspicious_dtype(datatype)\n        try:\n            dtype = NUMPY_DTYPE[datatype]\n        except KeyError:\n            return nii\n        else:\n            header.set_data_dtype(dtype)\n            converted = np.asanyarray(nii.dataobj, dtype=dtype)\n            return nib.Nifti1Image(converted, nii.affine, header)\n\n    def _run_interface(self, runtime):\n        \"\"\"\n        Execute this interface with the provided runtime.\n\n        TODO: Is the *runtime* argument required? It doesn't seem to be used\n              anywhere.\n\n        Parameters\n        ----------\n        runtime : Any\n            Execution runtime ?\n\n        Returns\n        -------\n        Any\n            Execution runtime ?\n        \"\"\"\n        # Squeeze 4th dimension if possible (#660)\n        nii = nib.squeeze_image(nib.load(self.inputs.in_file))\n\n        if self.inputs.check_ras:\n            nii = nib.as_closest_canonical(nii)\n\n        if self.inputs.check_dtype:\n            nii = self._check_dtype(nii)\n\n        # Generate name\n        out_file, ext = op.splitext(op.basename(self.inputs.in_file))\n        if ext == '.gz':\n            out_file, ext2 = op.splitext(out_file)\n            ext = ext2 + ext\n        out_file_name = OUT_FILE.format(prefix=out_file, ext=ext)\n        self._results['out_file'] = op.abspath(out_file_name)\n        nii.to_filename(self._results['out_file'])\n\n        return runtime\n"
  },
  {
    "path": "mriqc/interfaces/common/ensure_size.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nDefinition of the :class:`EnsureSize` interface.\n\"\"\"\n\nfrom os import path as op\n\nimport nibabel as nib\nimport numpy as np\nfrom nipype.interfaces.ants import ApplyTransforms\nfrom nipype.interfaces.base import (\n    BaseInterfaceInputSpec,\n    File,\n    SimpleInterface,\n    TraitedSpec,\n    isdefined,\n    traits,\n)\nfrom niworkflows.data import Loader\n\nfrom mriqc import config, messages\n\nOUT_FILE_NAME = '{prefix}_resampled{ext}'\nOUT_MASK_NAME = '{prefix}_resmask{ext}'\nREF_FILE_NAME = 'resample_ref.nii.gz'\nREF_MASK_NAME = 'mask_ref.nii.gz'\nload_data = Loader('mriqc')\n\n\nclass EnsureSizeInputSpec(BaseInterfaceInputSpec):\n    \"\"\"\n    Input specification for the :class:`EnsureSize` interface.\n    \"\"\"\n\n    in_file = File(exists=True, copyfile=False, mandatory=True, desc='input image')\n    in_mask = File(exists=True, copyfile=False, desc='input mask')\n    pixel_size = traits.Float(2.0, usedefault=True, desc='desired pixel size (mm)')\n\n\nclass EnsureSizeOutputSpec(TraitedSpec):\n    \"\"\"\n    Output specification for the :class:`EnsureSize` interface.\n    \"\"\"\n\n    out_file = File(exists=True, desc='output image')\n    out_mask = File(exists=True, desc='output mask')\n\n\nclass EnsureSize(SimpleInterface):\n    \"\"\"\n    Checks the size of the input image and resamples it to have `pixel_size`.\n    \"\"\"\n\n    input_spec = EnsureSizeInputSpec\n    output_spec = EnsureSizeOutputSpec\n\n    def _check_size(self, nii: nib.Nifti1Image) -> bool:\n        zooms = nii.header.get_zooms()\n        size_diff = np.array(zooms[:3]) - (self.inputs.pixel_size - 0.1)\n        if np.all(size_diff >= -1e-3):\n            config.loggers.interface.info(messages.VOXEL_SIZE_OK)\n            return True\n        else:\n            small_voxel_message = messages.VOXEL_SIZE_SMALL.format(\n                *zooms[:3], self.inputs.pixel_size, *size_diff\n            )\n            config.loggers.interface.info(small_voxel_message)\n            return False\n\n    def _run_interface(self, runtime):\n        nii = nib.load(self.inputs.in_file)\n        size_ok = self._check_size(nii)\n        if size_ok:\n            self._results['out_file'] = self.inputs.in_file\n            if isdefined(self.inputs.in_mask):\n                self._results['out_mask'] = self.inputs.in_mask\n        else:\n            # Figure out new matrix\n            # 1) Get base affine\n            aff_base = nii.header.get_base_affine()\n            aff_base_inv = np.linalg.inv(aff_base)\n\n            # 2) Find center pixel in mm\n            center_idx = (np.array(nii.shape[:3]) - 1) * 0.5\n            center_mm = aff_base.dot(center_idx.tolist() + [1])\n\n            # 3) Find extent of each dimension\n            min_mm = aff_base.dot([-0.5, -0.5, -0.5, 1])\n            max_mm = aff_base.dot((np.array(nii.shape[:3]) - 0.5).tolist() + [1])\n            extent_mm = np.abs(max_mm - min_mm)[:3]\n\n            # 4) Find new matrix size\n            new_size = np.array(extent_mm / self.inputs.pixel_size, dtype=int)\n\n            # 5) Initialize new base affine\n            new_base = aff_base[:3, :3] * np.abs(aff_base_inv[:3, :3]) * self.inputs.pixel_size\n\n            # 6) Find new center\n            new_center_idx = (new_size - 1) * 0.5\n            new_affine_base = np.eye(4)\n            new_affine_base[:3, :3] = new_base\n            new_affine_base[:3, 3] = center_mm[:3] - new_base.dot(new_center_idx)\n\n            # 7) Rotate new matrix\n            rotation = nii.affine.dot(aff_base_inv)\n            new_affine = rotation.dot(new_affine_base)\n\n            # 8) Generate new reference image\n            hdr = nii.header.copy()\n            hdr.set_data_shape(new_size)\n            nib.Nifti1Image(\n                np.zeros(new_size, dtype=nii.get_data_dtype()), new_affine, hdr\n            ).to_filename(REF_FILE_NAME)\n\n            out_prefix, ext = op.splitext(op.basename(self.inputs.in_file))\n            if ext == '.gz':\n                out_prefix, ext2 = op.splitext(out_prefix)\n                ext = ext2 + ext\n\n            out_file_name = OUT_FILE_NAME.format(prefix=out_prefix, ext=ext)\n            out_file = op.abspath(out_file_name)\n\n            # 9) Resample new image\n            ApplyTransforms(\n                dimension=3,\n                input_image=self.inputs.in_file,\n                reference_image=REF_FILE_NAME,\n                interpolation='LanczosWindowedSinc',\n                transforms=[str(load_data('data/itk_identity.tfm').absolute())],\n                output_image=out_file,\n            ).run()\n\n            self._results['out_file'] = out_file\n\n            if isdefined(self.inputs.in_mask):\n                hdr = nii.header.copy()\n                hdr.set_data_shape(new_size)\n                hdr.set_data_dtype(np.uint8)\n                nib.Nifti1Image(np.zeros(new_size, dtype=np.uint8), new_affine, hdr).to_filename(\n                    REF_MASK_NAME\n                )\n\n                out_mask_name = OUT_MASK_NAME.format(prefix=out_prefix, ext=ext)\n                out_mask = op.abspath(out_mask_name)\n                ApplyTransforms(\n                    dimension=3,\n                    input_image=self.inputs.in_mask,\n                    reference_image=REF_MASK_NAME,\n                    interpolation='NearestNeighbor',\n                    transforms=[str(load_data('data/itk_identity.tfm').absolute())],\n                    output_image=out_mask,\n                ).run()\n\n                self._results['out_mask'] = out_mask\n\n        return runtime\n"
  },
  {
    "path": "mriqc/interfaces/diffusion.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2023 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Interfaces for manipulating DWI data.\"\"\"\n\nfrom __future__ import annotations\n\nimport nibabel as nb\nimport numpy as np\nimport scipy.ndimage as nd\nfrom dipy.core.gradients import gradient_table\nfrom dipy.stats.qc import find_qspace_neighbors\nfrom nipype.interfaces.base import (\n    BaseInterfaceInputSpec as _BaseInterfaceInputSpec,\n)\nfrom nipype.interfaces.base import (\n    File,\n    InputMultiObject,\n    OutputMultiObject,\n    SimpleInterface,\n    TraitedSpec,\n    isdefined,\n    traits,\n)\nfrom nipype.interfaces.base import (\n    TraitedSpec as _TraitedSpec,\n)\nfrom nipype.utils.filemanip import fname_presuffix\nfrom niworkflows.interfaces.bids import ReadSidecarJSON, _ReadSidecarJSONOutputSpec\nfrom sklearn.cluster import KMeans\nfrom sklearn.model_selection import GridSearchCV\n\nfrom mriqc.utils.misc import _flatten_dict\n\n__all__ = (\n    'CCSegmentation',\n    'CorrectSignalDrift',\n    'DiffusionModel',\n    'DiffusionQC',\n    'ExtractOrientations',\n    'FilterShells',\n    'NumberOfShells',\n    'PIESNO',\n    'ReadDWIMetadata',\n    'RotateVectors',\n    'SpikingVoxelsMask',\n    'SplitShells',\n    'WeightedStat',\n)\n\n\nFD_THRESHOLD = 0.2\n\n\nclass _DiffusionQCInputSpec(_BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='original EPI 4D file')\n    in_b0 = File(exists=True, mandatory=True, desc='input b=0 average')\n    in_shells = InputMultiObject(\n        File(exists=True),\n        mandatory=True,\n        desc='DWI data after HMC and split by shells (indexed by in_bval)',\n    )\n    in_shells_bval = traits.List(\n        traits.Float,\n        minlen=1,\n        mandatory=True,\n        desc='list of unique b-values (one per shell), ordered by growing intensity',\n    )\n    in_bval_file = File(exists=True, mandatory=True, desc='original b-vals file')\n    in_bvec = traits.List(\n        traits.List(\n            traits.Tuple(traits.Float, traits.Float, traits.Float),\n            minlen=1,\n        ),\n        mandatory=True,\n        minlen=1,\n        desc='a list of shell-wise splits of b-vectors lists -- first list are b=0',\n    )\n    in_bvec_rotated = traits.List(\n        traits.Tuple(traits.Float, traits.Float, traits.Float),\n        mandatory=True,\n        minlen=1,\n        desc='b-vectors after rotating by the head-motion correction transform',\n    )\n    in_bvec_diff = traits.List(\n        traits.Float,\n        mandatory=True,\n        minlen=1,\n        desc='list of angle deviations from the original b-vectors table',\n    )\n    in_fa = File(exists=True, mandatory=True, desc='input FA map')\n    in_fa_nans = File(\n        exists=True, mandatory=True, desc='binary mask of NaN values in the \"raw\" FA map'\n    )\n    in_fa_degenerate = File(\n        exists=True,\n        mandatory=True,\n        desc='binary mask of values outside [0, 1] in the \"raw\" FA map',\n    )\n    in_cfa = File(exists=True, mandatory=True, desc='output color FA file')\n    in_md = File(exists=True, mandatory=True, desc='input MD map')\n    brain_mask = File(exists=True, mandatory=True, desc='input probabilistic brain mask')\n    wm_mask = File(exists=True, mandatory=True, desc='input probabilistic white-matter mask')\n    cc_mask = File(exists=True, mandatory=True, desc='input binary mask of the corpus callosum')\n    spikes_mask = File(exists=True, mandatory=True, desc='input binary mask of spiking voxels')\n    noise_floor = traits.Float(mandatory=True, desc='noise-floor map estimated by means of PCA')\n    direction = traits.Enum(\n        'all',\n        'x',\n        'y',\n        '-x',\n        '-y',\n        usedefault=True,\n        desc='direction for GSR computation',\n    )\n    in_fd = File(\n        exists=True,\n        mandatory=True,\n        desc='motion parameters for FD computation',\n    )\n    fd_thres = traits.Float(\n        FD_THRESHOLD,\n        usedefault=True,\n        desc='FD threshold for orientation exclusion based on head motion',\n    )\n    in_fwhm = traits.List(traits.Float, desc='smoothness estimated with AFNI')\n    qspace_neighbors = traits.List(\n        traits.Tuple(traits.Int, traits.Int),\n        mandatory=True,\n        minlen=1,\n        desc='q-space nearest neighbor pairs',\n    )\n    piesno_sigma = traits.Float(-1.0, usedefault=True, desc='noise sigma calculated with PIESNO')\n\n\nclass _DiffusionQCOutputSpec(TraitedSpec):\n    bdiffs = traits.Dict\n    efc = traits.Dict\n    fa_degenerate = traits.Float\n    fa_nans = traits.Float\n    fber = traits.Dict\n    fd = traits.Dict\n    ndc = traits.Float\n    sigma = traits.Dict\n    spikes = traits.Dict\n    # gsr = traits.Dict\n    # tsnr = traits.Float\n    # fwhm = traits.Dict(desc='full width half-maximum measure')\n    # size = traits.Dict\n    snr_cc = traits.Dict\n    summary = traits.Dict\n\n    out_qc = traits.Dict(desc='output flattened dictionary with all measures')\n\n\nclass DiffusionQC(SimpleInterface):\n    \"\"\"Computes :abbr:`QC (Quality Control)` measures on the input DWI EPI scan.\"\"\"\n\n    input_spec = _DiffusionQCInputSpec\n    output_spec = _DiffusionQCOutputSpec\n\n    def _run_interface(self, runtime):\n        from mriqc.qc import anatomical as aqc\n        from mriqc.qc import diffusion as dqc\n        # from mriqc.qc import functional as fqc\n\n        # Get the mean EPI data and get it ready\n        b0nii = nb.load(self.inputs.in_b0)\n        b0data = np.round(\n            np.nan_to_num(np.asanyarray(b0nii.dataobj)),\n            3,\n        )\n        b0data[b0data < 0] = 0\n\n        # Get the FA data and get it ready OE: enable when used\n        # fanii = nb.load(self.inputs.in_fa)\n        # fadata = np.round(\n        #     np.nan_to_num(np.asanyarray(fanii.dataobj)),\n        #     3,\n        # )\n\n        # Get brain mask data\n        msknii = nb.load(self.inputs.brain_mask)\n        mskdata = np.round(  # Protect the thresholding with a rounding for stability\n            msknii.get_fdata(),\n            3,\n        )\n        if np.sum(mskdata) < 100:\n            raise RuntimeError(\n                'Detected less than 100 voxels belonging to the brain mask. '\n                'MRIQC failed to process this dataset.'\n            )\n\n        # Get wm mask data\n        wmnii = nb.load(self.inputs.wm_mask)\n        wmdata = np.round(  # Protect the thresholding with a rounding for stability\n            np.asanyarray(wmnii.dataobj),\n            3,\n        )\n\n        # Get cc mask data\n        ccnii = nb.load(self.inputs.cc_mask)\n        ccdata = np.round(  # Protect the thresholding with a rounding for stability\n            np.asanyarray(ccnii.dataobj),\n            3,\n        )\n\n        # Get DWI data after splitting them by shell (DSI's data is clustered)\n        shelldata = [\n            np.round(\n                np.asanyarray(nb.load(s).dataobj),\n                4,\n            )\n            for s in self.inputs.in_shells\n        ]\n\n        # Summary stats\n        rois = {\n            'fg': mskdata,\n            'bg': 1.0 - mskdata,\n            'wm': wmdata,\n        }\n        stats = aqc.summary_stats(b0data, rois)\n        self._results['summary'] = stats\n\n        # CC mask SNR and std\n        self._results['snr_cc'], cc_sigma = dqc.cc_snr(\n            in_b0=b0data,\n            dwi_shells=shelldata,\n            cc_mask=ccdata,\n            b_values=self.inputs.in_shells_bval,\n            b_vectors=self.inputs.in_bvec,\n        )\n\n        fa_nans_mask = np.asanyarray(nb.load(self.inputs.in_fa_nans).dataobj) > 0.0\n        self._results['fa_nans'] = round(float(1e6 * fa_nans_mask[mskdata > 0.5].mean()), 2)\n\n        fa_degenerate_mask = np.asanyarray(nb.load(self.inputs.in_fa_degenerate).dataobj) > 0.0\n        self._results['fa_degenerate'] = round(\n            float(1e6 * fa_degenerate_mask[mskdata > 0.5].mean()),\n            2,\n        )\n\n        # Get spikes-mask data\n        spmask = np.asanyarray(nb.load(self.inputs.spikes_mask).dataobj) > 0.0\n        self._results['spikes'] = dqc.spike_ppm(spmask)\n\n        # FBER\n        self._results['fber'] = {\n            f'shell{i + 1:02d}': aqc.fber(bdata, mskdata.astype(np.uint8))\n            for i, bdata in enumerate(shelldata)\n        }\n\n        # EFC\n        self._results['efc'] = {\n            f'shell{i + 1:02d}': aqc.efc(bdata) for i, bdata in enumerate(shelldata)\n        }\n\n        # FD\n        fd_data = np.loadtxt(self.inputs.in_fd, skiprows=1)\n        num_fd = (fd_data > self.inputs.fd_thres).sum()\n        self._results['fd'] = {\n            'mean': round(float(fd_data.mean()), 4),\n            'num': int(num_fd),\n            'perc': float(num_fd * 100 / (len(fd_data) + 1)),\n        }\n\n        # NDC\n        dwidata = np.round(\n            np.nan_to_num(nb.load(self.inputs.in_file).get_fdata()),\n            3,\n        )\n        self._results['ndc'] = dqc.neighboring_dwi_correlation(\n            dwidata,\n            neighbor_indices=self.inputs.qspace_neighbors,\n            mask=mskdata > 0.5,\n        )\n\n        # Sigmas\n        self._results['sigma'] = {\n            'cc': round(float(cc_sigma), 4),\n            'piesno': round(self.inputs.piesno_sigma, 4),\n            'pca': round(self.inputs.noise_floor, 4),\n        }\n\n        # rotated b-vecs deviations\n        diffs = np.array(self.inputs.in_bvec_diff)\n        self._results['bdiffs'] = {\n            'mean': round(float(diffs[diffs > 1e-4].mean()), 4),\n            'median': round(float(np.median(diffs[diffs > 1e-4])), 4),\n            'max': round(float(diffs[diffs > 1e-4].max()), 4),\n            'min': round(float(diffs[diffs > 1e-4].min()), 4),\n        }\n\n        self._results['out_qc'] = _flatten_dict(self._results)\n        return runtime\n\n\nclass _ReadDWIMetadataOutputSpec(_ReadSidecarJSONOutputSpec):\n    out_bvec_file = File(desc='corresponding bvec file')\n    out_bval_file = File(desc='corresponding bval file')\n    out_bmatrix = traits.List(traits.List(traits.Float), desc='b-matrix')\n    qspace_neighbors = traits.List(\n        traits.Tuple(traits.Int, traits.Int),\n        desc='q-space nearest neighbor pairs',\n    )\n\n\nclass ReadDWIMetadata(ReadSidecarJSON):\n    \"\"\"\n    Extends the NiWorkflows' interface to extract bvec/bval from DWI datasets.\n    \"\"\"\n\n    output_spec = _ReadDWIMetadataOutputSpec\n\n    def _run_interface(self, runtime):\n        runtime = super()._run_interface(runtime)\n\n        self._results['out_bvec_file'] = str(self.layout.get_bvec(self.inputs.in_file))\n        self._results['out_bval_file'] = str(self.layout.get_bval(self.inputs.in_file))\n\n        bvecs = np.loadtxt(self._results['out_bvec_file']).T\n        bvals = np.loadtxt(self._results['out_bval_file'])\n\n        gtab = gradient_table(bvals, bvecs=bvecs)\n\n        self._results['qspace_neighbors'] = find_qspace_neighbors(gtab)\n        self._results['out_bmatrix'] = np.hstack((bvecs, bvals[:, np.newaxis])).tolist()\n\n        return runtime\n\n\nclass _WeightedStatInputSpec(_BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='an image')\n    in_weights = traits.List(\n        traits.Either(traits.Bool, traits.Float),\n        mandatory=True,\n        minlen=1,\n        desc='list of weights',\n    )\n    stat = traits.Enum('mean', 'std', usedefault=True, desc='statistic to compute')\n\n\nclass _WeightedStatOutputSpec(_TraitedSpec):\n    out_file = File(exists=True, desc='masked file')\n\n\nclass WeightedStat(SimpleInterface):\n    \"\"\"Weighted average of the input image across the last dimension.\"\"\"\n\n    input_spec = _WeightedStatInputSpec\n    output_spec = _WeightedStatOutputSpec\n\n    def _run_interface(self, runtime):\n        img = nb.load(self.inputs.in_file)\n        weights = [float(w) for w in self.inputs.in_weights]\n        data = np.asanyarray(img.dataobj)\n        statmap = np.average(data, weights=weights, axis=-1)\n\n        self._results['out_file'] = fname_presuffix(\n            self.inputs.in_file, suffix=f'_{self.inputs.stat}', newpath=runtime.cwd\n        )\n\n        if self.inputs.stat == 'std':\n            statmap = np.sqrt(\n                np.average((data - statmap[..., np.newaxis]) ** 2, weights=weights, axis=-1)\n            )\n\n        hdr = img.header.copy()\n        img.__class__(\n            statmap.astype(hdr.get_data_dtype()),\n            img.affine,\n            hdr,\n        ).to_filename(self._results['out_file'])\n\n        return runtime\n\n\nclass _NumberOfShellsInputSpec(_BaseInterfaceInputSpec):\n    in_bvals = File(mandatory=True, desc='bvals file')\n    b0_threshold = traits.Float(50, usedefault=True, desc='a threshold for the low-b values')\n    dsi_threshold = traits.Int(11, usedefault=True, desc='number of shells to call a dataset DSI')\n\n\nclass _NumberOfShellsOutputSpec(_TraitedSpec):\n    models = traits.List(traits.Int, minlen=1, desc='number of shells ordered by model fit')\n    n_shells = traits.Int(desc='number of shells')\n    out_data = traits.List(\n        traits.Float,\n        minlen=1,\n        desc=\"list of new b-values (e.g., after 'shell-ifying' DSI)\",\n    )\n    b_values = traits.List(\n        traits.Float,\n        minlen=1,\n        desc='list of ``n_shells`` b-values associated with each shell (only nonzero)',\n    )\n    b_masks = traits.List(\n        traits.List(traits.Bool, minlen=1),\n        minlen=1,\n        desc='list of ``n_shells`` b-value-wise masks',\n    )\n    b_indices = traits.List(\n        traits.List(traits.Int, minlen=1),\n        minlen=1,\n        desc='list of ``n_shells`` b-value-wise indices lists',\n    )\n    b_dict = traits.Dict(\n        traits.Int, traits.List(traits.Int), desc='a map of b-values (including b=0) and masks'\n    )\n\n\nclass NumberOfShells(SimpleInterface):\n    \"\"\"\n    Weighted average of the input image across the last dimension.\n\n    Examples\n    --------\n    >>> np.savetxt(\"test.bval\", [0] * 8 + [1000] * 12 + [2000] * 10)\n    >>> NumberOfShells(in_bvals=\"test.bval\").run().outputs.n_shells\n    2\n    >>> np.savetxt(\"test.bval\", [0] * 8 + [1000] * 12)\n    >>> NumberOfShells(in_bvals=\"test.bval\").run().outputs.n_shells\n    1\n    >>> np.savetxt(\"test.bval\", np.arange(0, 9001, 120))\n    >>> NumberOfShells(in_bvals=\"test.bval\").run().outputs.n_shells > 7\n    True\n\n    \"\"\"\n\n    input_spec = _NumberOfShellsInputSpec\n    output_spec = _NumberOfShellsOutputSpec\n\n    def _run_interface(self, runtime):\n        in_data = np.squeeze(np.loadtxt(self.inputs.in_bvals))\n        highb_mask = in_data > self.inputs.b0_threshold\n\n        original_bvals = sorted(set(np.rint(in_data[highb_mask]).astype(int)))\n        round_bvals = np.round(in_data, -2).astype(int)\n        shell_bvals = sorted(set(round_bvals[highb_mask]))\n\n        if len(shell_bvals) <= self.inputs.dsi_threshold:\n            self._results['n_shells'] = len(shell_bvals)\n            self._results['models'] = [self._results['n_shells']]\n            self._results['out_data'] = round_bvals.tolist()\n            self._results['b_values'] = shell_bvals\n        else:\n            # For datasets identified as DSI, fit a k-means\n            grid_search = GridSearchCV(\n                KMeans(), param_grid={'n_clusters': range(1, 10)}, scoring=_rms\n            ).fit(in_data[highb_mask].reshape(-1, 1))\n\n            results = np.array(\n                sorted(\n                    zip(\n                        grid_search.cv_results_['mean_test_score'] * -1.0,\n                        grid_search.cv_results_['param_n_clusters'],\n                    )\n                )\n            )\n\n            self._results['models'] = results[:, 1].astype(int).tolist()\n            self._results['n_shells'] = int(grid_search.best_params_['n_clusters'])\n\n            out_data = np.zeros_like(in_data)\n            predicted_shell = np.rint(\n                np.squeeze(\n                    grid_search.best_estimator_.cluster_centers_[\n                        grid_search.best_estimator_.predict(in_data[highb_mask].reshape(-1, 1))\n                    ],\n                )\n            ).astype(int)\n\n            # If estimated shells matches direct count, probably right -- do not change b-vals\n            if len(original_bvals) == self._results['n_shells']:\n                # Find closest b-values\n                indices = np.abs(predicted_shell[:, np.newaxis] - original_bvals).argmin(axis=1)\n                predicted_shell = original_bvals[indices]\n\n            out_data[highb_mask] = predicted_shell\n            self._results['out_data'] = np.round(out_data.astype(float), 2).tolist()\n            self._results['b_values'] = sorted(\n                np.unique(np.round(predicted_shell.astype(float), 2)).tolist()\n            )\n\n        self._results['b_masks'] = [(~highb_mask).tolist()] + [\n            np.isclose(self._results['out_data'], bvalue).tolist()\n            for bvalue in self._results['b_values']\n        ]\n        self._results['b_indices'] = [\n            np.atleast_1d(np.squeeze(np.argwhere(b_mask)).astype(int)).tolist()\n            for b_mask in self._results['b_masks']\n        ]\n\n        self._results['b_dict'] = {\n            int(round(k, 0)): value\n            for k, value in zip([0] + self._results['b_values'], self._results['b_indices'])\n        }\n        return runtime\n\n\nclass _ExtractOrientationsInputSpec(_BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='dwi file')\n    indices = traits.List(traits.Int, mandatory=True, desc='indices to be extracted')\n    in_bvec_file = File(exists=True, desc='b-vectors file')\n\n\nclass _ExtractOrientationsOutputSpec(_TraitedSpec):\n    out_file = File(exists=True, desc='output b0 file')\n    out_bvec = traits.List(\n        traits.Tuple(traits.Float, traits.Float, traits.Float),\n        minlen=1,\n        desc='b-vectors',\n    )\n\n\nclass ExtractOrientations(SimpleInterface):\n    \"\"\"Extract all b=0 volumes from a dwi series.\"\"\"\n\n    input_spec = _ExtractOrientationsInputSpec\n    output_spec = _ExtractOrientationsOutputSpec\n\n    def _run_interface(self, runtime):\n        from nipype.utils.filemanip import fname_presuffix\n\n        out_file = fname_presuffix(\n            self.inputs.in_file,\n            suffix='_subset',\n            newpath=runtime.cwd,\n        )\n\n        self._results['out_file'] = out_file\n\n        img = nb.load(self.inputs.in_file)\n        bzeros = np.squeeze(np.asanyarray(img.dataobj)[..., self.inputs.indices])\n\n        hdr = img.header.copy()\n        hdr.set_data_shape(bzeros.shape)\n        hdr.set_xyzt_units('mm')\n        nb.Nifti1Image(bzeros, img.affine, hdr).to_filename(out_file)\n\n        if isdefined(self.inputs.in_bvec_file):\n            bvecs = np.loadtxt(self.inputs.in_bvec_file)[:, self.inputs.indices].T\n            self._results['out_bvec'] = [tuple(row) for row in bvecs]\n\n        return runtime\n\n\nclass _CorrectSignalDriftInputSpec(_BaseInterfaceInputSpec):\n    in_file = File(\n        exists=True,\n        mandatory=True,\n        desc='a 4D file with (exclusively) realigned low-b volumes',\n    )\n    bias_file = File(exists=True, desc='a B1 bias field')\n    brainmask_file = File(exists=True, desc='a 3D file of the brain mask')\n    b0_ixs = traits.List(traits.Int, mandatory=True, desc='Index of b0s')\n    bval_file = File(exists=True, mandatory=True, desc='bvalues file')\n    full_epi = File(exists=True, desc='a whole DWI dataset to be corrected for drift')\n\n\nclass _CorrectSignalDriftOutputSpec(_TraitedSpec):\n    out_file = File(desc='a 4D file with (exclusively) realigned, drift-corrected low-b volumes')\n    out_full_file = File(desc='full DWI input after drift correction')\n    b0_drift = traits.List(traits.Float, desc='global signal evolution')\n    signal_drift = traits.List(traits.Float, desc='signal drift after fiting exp decay')\n\n\nclass CorrectSignalDrift(SimpleInterface):\n    \"\"\"Correct DWI for signal drift.\"\"\"\n\n    input_spec = _CorrectSignalDriftInputSpec\n    output_spec = _CorrectSignalDriftOutputSpec\n\n    def _run_interface(self, runtime):\n        from mriqc import config\n\n        bvals = np.loadtxt(self.inputs.bval_file)\n        len_dmri = bvals.size\n\n        img = nb.load(self.inputs.in_file)\n        data = img.get_fdata()\n        bmask = np.ones_like(data[..., 0], dtype=bool)\n\n        # Correct for the B1 bias\n        if isdefined(self.inputs.bias_file):\n            data *= nb.load(self.inputs.bias_file).get_fdata()[..., np.newaxis]\n\n        if isdefined(self.inputs.brainmask_file):\n            bmask = np.round(nb.load(self.inputs.brainmask_file).get_fdata(), 2) > 0.5\n\n        self._results['out_file'] = fname_presuffix(\n            self.inputs.in_file, suffix='_nodrift', newpath=runtime.cwd\n        )\n\n        if (b0len := int(data.ndim < 4)) or (b0len := data.shape[3]) < 3:\n            config.loggers.interface.warn(\n                f'Insufficient number of low-b orientations ({b0len}) '\n                'to safely calculate signal drift.'\n            )\n\n            img.__class__(\n                np.round(data.astype('float32'), 4),\n                img.affine,\n                img.header,\n            ).to_filename(self._results['out_file'])\n\n            if isdefined(self.inputs.full_epi):\n                self._results['out_full_file'] = self.inputs.full_epi\n\n            self._results['b0_drift'] = [1.0] * b0len\n            self._results['signal_drift'] = [1.0] * len_dmri\n\n            return runtime\n\n        global_signal = np.array(\n            [np.median(data[..., n_b0][bmask]) for n_b0 in range(img.shape[-1])]\n        ).astype('float32')\n\n        # Normalize and correct\n        global_signal /= global_signal[0]\n        self._results['b0_drift'] = [round(float(gs), 4) for gs in global_signal]\n\n        config.loggers.interface.info(\n            f'Correcting drift with {len(global_signal)} b=0 volumes, with '\n            'global signal estimated at '\n            f'{\", \".join([str(v) for v in self._results[\"b0_drift\"]])}.'\n        )\n\n        data *= 1.0 / global_signal[np.newaxis, np.newaxis, np.newaxis, :]\n\n        img.__class__(\n            data.astype(img.header.get_data_dtype()),\n            img.affine,\n            img.header,\n        ).to_filename(self._results['out_file'])\n\n        # Fit line to log-transformed drifts\n        K, A_log = np.polyfit(self.inputs.b0_ixs, np.log(global_signal), 1)\n\n        t_points = np.arange(len_dmri, dtype=int)\n        fitted = np.squeeze(_exp_func(t_points, np.exp(A_log), K, 0))\n        self._results['signal_drift'] = fitted.astype(float).tolist()\n\n        if isdefined(self.inputs.full_epi):\n            self._results['out_full_file'] = fname_presuffix(\n                self.inputs.full_epi, suffix='_nodriftfull', newpath=runtime.cwd\n            )\n            full_img = nb.load(self.inputs.full_epi)\n            full_img.__class__(\n                full_img.get_fdata() * fitted[np.newaxis, np.newaxis, np.newaxis, :],\n                full_img.affine,\n                full_img.header,\n            ).to_filename(self._results['out_full_file'])\n        return runtime\n\n\nclass _SplitShellsInputSpec(_BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='dwi file')\n    bvals = traits.List(traits.Float, mandatory=True, desc='bval table')\n\n\nclass _SplitShellsOutputSpec(_TraitedSpec):\n    out_file = OutputMultiObject(File(exists=True), desc='output b0 file')\n\n\nclass SplitShells(SimpleInterface):\n    \"\"\"Split a DWI dataset into .\"\"\"\n\n    input_spec = _SplitShellsInputSpec\n    output_spec = _SplitShellsOutputSpec\n\n    def _run_interface(self, runtime):\n        from nipype.utils.filemanip import fname_presuffix\n\n        bval_list = np.rint(self.inputs.bvals).astype(int)\n        bvals = np.unique(bval_list)\n        img = nb.load(self.inputs.in_file)\n        data = np.asanyarray(img.dataobj)\n\n        self._results['out_file'] = []\n\n        for bval in bvals:\n            fname = fname_presuffix(\n                self.inputs.in_file, suffix=f'_b{bval:05d}', newpath=runtime.cwd\n            )\n            self._results['out_file'].append(fname)\n\n            img.__class__(\n                data[..., np.argwhere(bval_list == bval)],\n                img.affine,\n                img.header,\n            ).to_filename(fname)\n        return runtime\n\n\nclass _FilterShellsInputSpec(_BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='dwi file')\n    bvals = traits.List(traits.Float, mandatory=True, desc='bval table')\n    bvec_file = File(exists=True, mandatory=True, desc='b-vectors')\n    b_threshold = traits.Float(1100, usedefault=True, desc='b-values threshold')\n\n\nclass _FilterShellsOutputSpec(_TraitedSpec):\n    out_file = File(exists=True, desc='filtered DWI file')\n    out_bvals = traits.List(traits.Float, desc='filtered bvalues')\n    out_bvec_file = File(exists=True, desc='filtered bvecs file')\n    out_bval_file = File(exists=True, desc='filtered bvals file')\n\n\nclass FilterShells(SimpleInterface):\n    \"\"\"Extract DWIs below a given b-value threshold.\"\"\"\n\n    input_spec = _FilterShellsInputSpec\n    output_spec = _FilterShellsOutputSpec\n\n    def _run_interface(self, runtime):\n        from nipype.utils.filemanip import fname_presuffix\n\n        bvals = np.array(self.inputs.bvals)\n        bval_mask = bvals < self.inputs.b_threshold\n        bvecs = np.loadtxt(self.inputs.bvec_file)[:, bval_mask]\n\n        self._results['out_bvals'] = bvals[bval_mask].astype(float).tolist()\n        self._results['out_bvec_file'] = fname_presuffix(\n            self.inputs.in_file,\n            suffix='_dti.bvec',\n            newpath=runtime.cwd,\n            use_ext=False,\n        )\n        np.savetxt(self._results['out_bvec_file'], bvecs)\n\n        self._results['out_bval_file'] = fname_presuffix(\n            self.inputs.in_file,\n            suffix='_dti.bval',\n            newpath=runtime.cwd,\n            use_ext=False,\n        )\n        np.savetxt(self._results['out_bval_file'], bvals)\n\n        self._results['out_file'] = fname_presuffix(\n            self.inputs.in_file,\n            suffix='_dti',\n            newpath=runtime.cwd,\n        )\n\n        dwi_img = nb.load(self.inputs.in_file)\n        data = np.array(dwi_img.dataobj, dtype=dwi_img.header.get_data_dtype())[..., bval_mask]\n        dwi_img.__class__(\n            data,\n            dwi_img.affine,\n            dwi_img.header,\n        ).to_filename(self._results['out_file'])\n\n        return runtime\n\n\nclass _DiffusionModelInputSpec(_BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='dwi file')\n    bvals = traits.List(traits.Float, mandatory=True, desc='bval table')\n    bvec_file = File(exists=True, mandatory=True, desc='b-vectors')\n    brain_mask = File(exists=True, desc='brain mask file')\n    decimals = traits.Int(3, usedefault=True, desc='round output maps for reliability')\n    n_shells = traits.Int(mandatory=True, desc='number of shells')\n\n\nclass _DiffusionModelOutputSpec(_TraitedSpec):\n    out_fa = File(exists=True, desc='output FA file')\n    out_fa_nans = File(exists=True, desc='binary mask of NaN values in the \"raw\" FA map')\n    out_fa_degenerate = File(\n        exists=True,\n        desc='binary mask of values outside [0, 1] in the \"raw\" FA map',\n    )\n    out_cfa = File(exists=True, desc='output color FA file')\n    out_md = File(exists=True, desc='output MD file')\n\n\nclass DiffusionModel(SimpleInterface):\n    \"\"\"\n    Fit a :obj:`~dipy.reconst.dki.DiffusionKurtosisModel` on the dataset.\n\n    If ``n_shells`` is set to 1, then a :obj:`~dipy.reconst.dti.TensorModel`\n    is used.\n\n    \"\"\"\n\n    input_spec = _DiffusionModelInputSpec\n    output_spec = _DiffusionModelOutputSpec\n\n    def _run_interface(self, runtime):\n        from dipy.core.gradients import gradient_table_from_bvals_bvecs\n        from nipype.utils.filemanip import fname_presuffix\n\n        bvals = np.array(self.inputs.bvals)\n\n        gtab = gradient_table_from_bvals_bvecs(\n            bvals=bvals,\n            bvecs=np.loadtxt(self.inputs.bvec_file).T,\n        )\n\n        img = nb.load(self.inputs.in_file)\n        data = img.get_fdata(dtype='float32')\n\n        brainmask = np.ones_like(data[..., 0], dtype=bool)\n\n        if isdefined(self.inputs.brain_mask):\n            brainmask = (\n                np.round(\n                    nb.load(self.inputs.brain_mask).get_fdata(),\n                    3,\n                )\n                > 0.5\n            )\n\n        if self.inputs.n_shells == 1:\n            from dipy.reconst.dti import TensorModel as Model\n        else:\n            from dipy.reconst.dki import DiffusionKurtosisModel as Model\n\n        # Fit DIT\n        fwdtifit = Model(gtab).fit(\n            data,\n            mask=brainmask,\n        )\n\n        # Extract the FA\n        fa_data = fwdtifit.fa\n        fa_nan_msk = np.isnan(fa_data)\n        fa_data[fa_nan_msk] = 0\n\n        # Round for stability\n        fa_data = np.round(fa_data, self.inputs.decimals)\n        degenerate_msk = (fa_data < 0) | (fa_data > 1.0)\n        # Clamp the FA to remove degenerate\n        fa_data = np.clip(fa_data, 0, 1)\n\n        fa_nii = nb.Nifti1Image(\n            fa_data,\n            img.affine,\n            None,\n        )\n\n        fa_nii.header.set_xyzt_units('mm')\n        fa_nii.header.set_intent('estimate', name='Fractional Anisotropy (FA)')\n\n        self._results['out_fa'] = fname_presuffix(\n            self.inputs.in_file,\n            suffix='fa',\n            newpath=runtime.cwd,\n        )\n\n        fa_nii.to_filename(self._results['out_fa'])\n\n        # Write out degenerate and nans masks\n        fa_nan_nii = nb.Nifti1Image(\n            fa_nan_msk.astype(np.uint8),\n            img.affine,\n            None,\n        )\n\n        fa_nan_nii.header.set_xyzt_units('mm')\n        fa_nan_nii.header.set_intent('estimate', name='NaNs in the FA map mask')\n        fa_nan_nii.header['cal_max'] = 1\n        fa_nan_nii.header['cal_min'] = 0\n\n        self._results['out_fa_nans'] = fname_presuffix(\n            self.inputs.in_file,\n            suffix='desc-fanans_mask',\n            newpath=runtime.cwd,\n        )\n        fa_nan_nii.to_filename(self._results['out_fa_nans'])\n\n        fa_degenerate_nii = nb.Nifti1Image(\n            degenerate_msk.astype(np.uint8),\n            img.affine,\n            None,\n        )\n\n        fa_degenerate_nii.header.set_xyzt_units('mm')\n        fa_degenerate_nii.header.set_intent(\n            'estimate', name='degenerate vectors in the FA map mask'\n        )\n        fa_degenerate_nii.header['cal_max'] = 1\n        fa_degenerate_nii.header['cal_min'] = 0\n\n        self._results['out_fa_degenerate'] = fname_presuffix(\n            self.inputs.in_file,\n            suffix='desc-fadegenerate_mask',\n            newpath=runtime.cwd,\n        )\n        fa_degenerate_nii.to_filename(self._results['out_fa_degenerate'])\n\n        # Extract the color FA\n        cfa_data = fwdtifit.color_fa\n        cfa_nii = nb.Nifti1Image(\n            np.clip(cfa_data, a_min=0.0, a_max=1.0),\n            img.affine,\n            None,\n        )\n\n        cfa_nii.header.set_xyzt_units('mm')\n        cfa_nii.header.set_intent('estimate', name='Fractional Anisotropy (FA)')\n        cfa_nii.header['cal_max'] = 1.0\n        cfa_nii.header['cal_min'] = 0.0\n\n        self._results['out_cfa'] = fname_presuffix(\n            self.inputs.in_file,\n            suffix='cfa',\n            newpath=runtime.cwd,\n        )\n        cfa_nii.to_filename(self._results['out_cfa'])\n\n        # Extract the AD\n        self._results['out_md'] = fname_presuffix(\n            self.inputs.in_file,\n            suffix='md',\n            newpath=runtime.cwd,\n        )\n        md_data = np.array(fwdtifit.md, dtype='float32')\n        md_data[np.isnan(md_data)] = 0\n        md_data = np.clip(md_data, 0, 1)\n        md_hdr = fa_nii.header.copy()\n        md_hdr.set_intent('estimate', name='Mean diffusivity (MD)')\n        nb.Nifti1Image(md_data, img.affine, md_hdr).to_filename(self._results['out_md'])\n\n        return runtime\n\n\nclass _CCSegmentationInputSpec(_BaseInterfaceInputSpec):\n    in_fa = File(exists=True, mandatory=True, desc='fractional anisotropy (FA) file')\n    in_cfa = File(exists=True, mandatory=True, desc='color FA file')\n    min_rgb = traits.Tuple(\n        (0.4, 0.008, 0.008),\n        types=(traits.Float,) * 3,\n        usedefault=True,\n        desc='minimum RGB within the CC',\n    )\n    max_rgb = traits.Tuple(\n        (1.1, 0.25, 0.25),\n        types=(traits.Float,) * 3,\n        usedefault=True,\n        desc='maximum RGB within the CC',\n    )\n    wm_threshold = traits.Float(0.35, usedefault=True, desc='WM segmentation threshold')\n    clean_mask = traits.Bool(False, usedefault=True, desc='run a final cleanup step on mask')\n\n\nclass _CCSegmentationOutputSpec(_TraitedSpec):\n    out_mask = File(exists=True, desc='output mask of the corpus callosum')\n    wm_mask = File(exists=True, desc='output mask of the white-matter (thresholded)')\n    wm_finalmask = File(exists=True, desc='output mask of the white-matter after binary opening')\n\n\nclass CCSegmentation(SimpleInterface):\n    \"\"\"Computes :abbr:`QC (Quality Control)` measures on the input DWI EPI scan.\"\"\"\n\n    input_spec = _CCSegmentationInputSpec\n    output_spec = _CCSegmentationOutputSpec\n\n    def _run_interface(self, runtime):\n        from skimage.measure import label\n\n        self._results['out_mask'] = fname_presuffix(\n            self.inputs.in_cfa,\n            suffix='ccmask',\n            newpath=runtime.cwd,\n        )\n        self._results['wm_mask'] = fname_presuffix(\n            self.inputs.in_cfa,\n            suffix='wmmask',\n            newpath=runtime.cwd,\n        )\n        self._results['wm_finalmask'] = fname_presuffix(\n            self.inputs.in_cfa,\n            suffix='wmfinalmask',\n            newpath=runtime.cwd,\n        )\n\n        fa_nii = nb.load(self.inputs.in_fa)\n        fa_data = np.round(fa_nii.get_fdata(dtype='float32'), 4)\n        fa_labels = label((fa_data > self.inputs.wm_threshold).astype(np.uint8))\n        wm_mask = fa_labels == np.argmax(np.bincount(fa_labels.flat)[1:]) + 1\n\n        # Write out binary WM mask\n        wm_mask_nii = nb.Nifti1Image(\n            wm_mask.astype(np.uint8),\n            fa_nii.affine,\n            None,\n        )\n        wm_mask_nii.header.set_xyzt_units('mm')\n        wm_mask_nii.header.set_intent('estimate', name='white-matter mask (FA thresholded)')\n        wm_mask_nii.header['cal_max'] = 1\n        wm_mask_nii.header['cal_min'] = 0\n        wm_mask_nii.to_filename(self._results['wm_mask'])\n\n        # Massage FA with greyscale mathematical morphology\n        struct = nd.generate_binary_structure(wm_mask.ndim, wm_mask.ndim - 1)\n        # Perform a closing followed by opening operations on the FA.\n        wm_mask = nd.grey_closing(\n            fa_data,\n            structure=struct,\n        )\n        wm_mask = nd.grey_opening(\n            wm_mask,\n            structure=struct,\n        )\n\n        fa_labels = label((np.round(wm_mask, 4) > self.inputs.wm_threshold).astype(np.uint8))\n        wm_mask = fa_labels == np.argmax(np.bincount(fa_labels.flat)[1:]) + 1\n\n        # Write out binary WM mask after binary opening\n        wm_mask_nii = nb.Nifti1Image(\n            wm_mask.astype(np.uint8),\n            fa_nii.affine,\n            wm_mask_nii.header,\n        )\n        wm_mask_nii.header.set_intent('estimate', name='white-matter mask after binary opening')\n        wm_mask_nii.to_filename(self._results['wm_finalmask'])\n\n        cfa_data = np.round(nb.load(self.inputs.in_cfa).get_fdata(dtype='float32'), 4)\n        for i in range(cfa_data.shape[-1]):\n            cfa_data[..., i] = nd.grey_closing(\n                cfa_data[..., i],\n                structure=struct,\n            )\n            cfa_data[..., i] = nd.grey_opening(\n                cfa_data[..., i],\n                structure=struct,\n            )\n\n        cc_mask = segment_corpus_callosum(\n            in_cfa=cfa_data,\n            mask=wm_mask,\n            min_rgb=self.inputs.min_rgb,\n            max_rgb=self.inputs.max_rgb,\n            clean_mask=self.inputs.clean_mask,\n        )\n        cc_mask_nii = nb.Nifti1Image(\n            cc_mask.astype(np.uint8),\n            fa_nii.affine,\n            None,\n        )\n        cc_mask_nii.header.set_xyzt_units('mm')\n        cc_mask_nii.header.set_intent('estimate', name='corpus callosum mask')\n        cc_mask_nii.header['cal_max'] = 1\n        cc_mask_nii.header['cal_min'] = 0\n        cc_mask_nii.to_filename(self._results['out_mask'])\n        return runtime\n\n\nclass _SpikingVoxelsMaskInputSpec(_BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='a DWI 4D file')\n    brain_mask = File(exists=True, mandatory=True, desc='input probabilistic brain 3D mask')\n    z_threshold = traits.Float(3.0, usedefault=True, desc='z-score threshold')\n    b_masks = traits.List(\n        traits.List(traits.Int, minlen=1),\n        minlen=1,\n        mandatory=True,\n        desc='list of ``n_shells`` b-value-wise indices lists',\n    )\n\n\nclass _SpikingVoxelsMaskOutputSpec(_TraitedSpec):\n    out_mask = File(exists=True, desc='a 4D binary mask of spiking voxels')\n\n\nclass SpikingVoxelsMask(SimpleInterface):\n    \"\"\"Computes :abbr:`QC (Quality Control)` measures on the input DWI EPI scan.\"\"\"\n\n    input_spec = _SpikingVoxelsMaskInputSpec\n    output_spec = _SpikingVoxelsMaskOutputSpec\n\n    def _run_interface(self, runtime):\n        self._results['out_mask'] = fname_presuffix(\n            self.inputs.in_file,\n            suffix='spikesmask',\n            newpath=runtime.cwd,\n        )\n\n        in_nii = nb.load(self.inputs.in_file)\n        data = np.round(in_nii.get_fdata(), 4).astype('float32')\n\n        bmask_nii = nb.load(self.inputs.brain_mask)\n        brainmask = np.round(bmask_nii.get_fdata(), 2).astype('float32')\n\n        spikes_mask = get_spike_mask(\n            data,\n            shell_masks=self.inputs.b_masks,\n            brainmask=brainmask,\n            z_threshold=self.inputs.z_threshold,\n        )\n\n        header = bmask_nii.header.copy()\n        header.set_data_dtype(np.uint8)\n        header.set_xyzt_units('mm')\n        header.set_intent('estimate', name='spiking voxels mask')\n        header['cal_max'] = 1\n        header['cal_min'] = 0\n\n        # Write out binary WM mask after binary opening\n        spikes_mask_nii = nb.Nifti1Image(\n            spikes_mask.astype(np.uint8),\n            bmask_nii.affine,\n            header,\n        )\n        spikes_mask_nii.to_filename(self._results['out_mask'])\n\n        return runtime\n\n\nclass _PIESNOInputSpec(_BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='a DWI 4D file')\n    n_channels = traits.Int(4, usedefault=True, min=1, desc='number of channels')\n\n\nclass _PIESNOOutputSpec(_TraitedSpec):\n    sigma = traits.Float(desc='noise sigma calculated with PIESNO')\n    out_mask = File(exists=True, desc='a 4D binary mask of spiking voxels')\n\n\nclass PIESNO(SimpleInterface):\n    \"\"\"Computes :abbr:`QC (Quality Control)` measures on the input DWI EPI scan.\"\"\"\n\n    input_spec = _PIESNOInputSpec\n    output_spec = _PIESNOOutputSpec\n\n    def _run_interface(self, runtime):\n        self._results['out_mask'] = fname_presuffix(\n            self.inputs.in_file,\n            suffix='piesno',\n            newpath=runtime.cwd,\n        )\n\n        in_nii = nb.load(self.inputs.in_file)\n        data = np.round(in_nii.get_fdata(), 4).astype('float32')\n\n        sigma, maskdata = noise_piesno(data)\n\n        header = in_nii.header.copy()\n        header.set_data_dtype(np.uint8)\n        header.set_xyzt_units('mm')\n        header.set_intent('estimate', name='PIESNO noise voxels mask')\n        header['cal_max'] = 1\n        header['cal_min'] = 0\n\n        nb.Nifti1Image(\n            maskdata.astype(np.uint8),\n            in_nii.affine,\n            header,\n        ).to_filename(self._results['out_mask'])\n\n        self._results['sigma'] = round(float(np.median(sigma)), 5)\n        return runtime\n\n\nclass _RotateVectorsInputSpec(_BaseInterfaceInputSpec):\n    in_file = File(\n        exists=True,\n        mandatory=True,\n        desc='TSV file containing original b-vectors and b-values',\n    )\n    reference = File(\n        exists=True,\n        mandatory=True,\n        desc='dwi-related file providing the reference affine',\n    )\n    transforms = File(exists=True, desc='list of head-motion transforms')\n\n\nclass _RotateVectorsOutputSpec(_TraitedSpec):\n    out_bvec = traits.List(\n        traits.Tuple(traits.Float, traits.Float, traits.Float),\n        minlen=1,\n        desc='rotated b-vectors',\n    )\n    out_diff = traits.List(\n        traits.Float,\n        minlen=1,\n        desc='angles in radians between new b-vectors and the original ones',\n    )\n\n\nclass RotateVectors(SimpleInterface):\n    \"\"\"Extract all b=0 volumes from a dwi series.\"\"\"\n\n    input_spec = _RotateVectorsInputSpec\n    output_spec = _RotateVectorsOutputSpec\n\n    def _run_interface(self, runtime):\n        from nitransforms.linear import load\n\n        vox2ras = nb.load(self.inputs.reference).affine\n        ras2vox = np.linalg.inv(vox2ras)\n\n        ijk = np.loadtxt(self.inputs.in_file).T\n        nonzero = np.linalg.norm(ijk, axis=1) > 1e-3\n\n        xyz = (vox2ras[:3, :3] @ ijk.T).T\n\n        # Unit vectors in RAS coordinates\n        xyz_norms = np.linalg.norm(xyz, axis=1)\n        xyz[nonzero] = xyz[nonzero] / xyz_norms[nonzero, np.newaxis]\n\n        hmc_rot = load(self.inputs.transforms).matrix[:, :3, :3]\n        ijk_rotated = (ras2vox[:3, :3] @ np.einsum('ijk,ik->ij', hmc_rot, xyz).T).T.astype(\n            'float32'\n        )\n        ijk_rotated_norm = np.linalg.norm(ijk_rotated, axis=1)\n        ijk_rotated[nonzero] = ijk_rotated[nonzero] / ijk_rotated_norm[nonzero, np.newaxis]\n        ijk_rotated[~nonzero] = ijk[~nonzero]\n\n        self._results['out_bvec'] = list(\n            zip(ijk_rotated[:, 0], ijk_rotated[:, 1], ijk_rotated[:, 2])\n        )\n\n        diffs = np.zeros_like(ijk[:, 0])\n        diffs[nonzero] = np.arccos(\n            np.clip(np.einsum('ij, ij->i', ijk[nonzero], ijk_rotated[nonzero]), -1.0, 1.0)\n        )\n        self._results['out_diff'] = [round(float(v), 6) for v in diffs]\n\n        return runtime\n\n\ndef _rms(estimator, X):\n    \"\"\"\n    Callable to pass to GridSearchCV that will calculate a distance score.\n\n    To consider: using `MDL\n    <https://erikerlandson.github.io/blog/2016/08/03/x-medoids-using-minimum-description-length-to-identify-the-k-in-k-medoids/>`__\n\n    \"\"\"\n    if len(np.unique(estimator.cluster_centers_)) < estimator.n_clusters:\n        return -np.inf\n\n    # Calculate distance from assigned shell centroid\n    distance = X - estimator.cluster_centers_[estimator.predict(X)]\n    # Make negative so CV optimizes minimizes the error\n    return -np.sqrt(distance**2).sum()\n\n\ndef _exp_func(t, A, K, C):\n    return A * np.exp(K * t) + C\n\n\ndef segment_corpus_callosum(\n    in_cfa: np.ndarray,\n    mask: np.ndarray,\n    min_rgb: tuple[float, float, float] = (0.6, 0.0, 0.0),\n    max_rgb: tuple[float, float, float] = (1.0, 0.1, 0.1),\n    clean_mask: bool = False,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"\n    Segments the corpus callosum (CC) from a color FA map.\n\n    Parameters\n    ----------\n    in_cfa : :obj:`~numpy.ndarray`\n        The color FA (cFA) map.\n    mask : :obj:`~numpy.ndarray` (bool, 3D)\n        A white matter mask used to define the initial bounding box.\n    min_rgb : :obj:`tuple`, optional\n        Minimum RGB values.\n    max_rgb : :obj:`tuple`, optional\n        Maximum RGB values.\n    clean_mask : :obj:`bool`, optional\n        Whether the CC mask is finally cleaned-up for spurious off voxels with\n        :obj:`dipy.segment.mask.clean_cc_mask`\n\n    Returns\n    -------\n    cc_mask: :obj:`~numpy.ndarray`\n        The final binary mask of the segmented CC.\n\n    Notes\n    -----\n    This implementation was derived from\n    :obj:`dipy.segment.mask.segment_from_cfa`.\n\n    \"\"\"\n    from dipy.segment.mask import bounding_box\n\n    # Prepare a bounding box of the CC\n    cc_box = np.zeros_like(mask, dtype=bool)\n    mins, maxs = bounding_box(mask)  # mask needs to be volume\n    mins = np.array(mins)\n    maxs = np.array(maxs)\n    diff = (maxs - mins) // 5\n    bounds_min = mins + diff\n    bounds_max = maxs - diff\n    cc_box[\n        bounds_min[0] : bounds_max[0], bounds_min[1] : bounds_max[1], bounds_min[2] : bounds_max[2]\n    ] = True\n\n    min_rgb = np.array(min_rgb)\n    max_rgb = np.array(max_rgb)\n\n    # Threshold color FA\n    cc_mask = np.all(\n        (in_cfa >= min_rgb[None, :]) & (in_cfa <= max_rgb[None, :]),\n        axis=-1,\n    )\n\n    # Apply bounding box and WM mask\n    cc_mask *= cc_box & mask\n\n    struct = nd.generate_binary_structure(cc_mask.ndim, cc_mask.ndim - 1)\n    # Perform a closing followed by opening operations on the FA.\n    cc_mask = nd.binary_closing(\n        cc_mask,\n        structure=struct,\n    )\n    cc_mask = nd.binary_opening(\n        cc_mask,\n        structure=struct,\n    )\n\n    if clean_mask:\n        from dipy.segment.mask import clean_cc_mask\n\n        cc_mask = clean_cc_mask(cc_mask)\n    return cc_mask\n\n\ndef get_spike_mask(\n    data: np.ndarray,\n    shell_masks: list,\n    brainmask: np.ndarray,\n    z_threshold: float = 3.0,\n) -> np.ndarray:\n    \"\"\"\n    Creates a binary mask classifying voxels in the data array as spike or non-spike.\n\n    This function identifies voxels with signal intensities exceeding a threshold based\n    on standard deviations above the mean. The threshold can be applied globally to\n    the entire data array, or it can be calculated for groups of voxels defined by\n    the ``grouping_vals`` parameter.\n\n    Parameters\n    ----------\n    data : :obj:`~numpy.ndarray`\n        The data array to be thresholded.\n    z_threshold : :obj:`float`, optional (default=3.0)\n        The number of standard deviations to use above the mean as the threshold\n        multiplier.\n    brainmask : :obj:`~numpy.ndarray`\n        The brain mask.\n    shell_masks : :obj:`list`\n        A list of :obj:`~numpy.ndarray` objects\n\n    Returns:\n    -------\n    spike_mask : :obj:`~numpy.ndarray`\n        A binary mask where ``True`` values indicate voxels classified as spikes and\n        ``False`` values indicate non-spikes. The mask has the same shape as the input\n        data array.\n\n    \"\"\"\n\n    spike_mask = np.zeros_like(data, dtype=bool)\n\n    brainmask = brainmask >= 0.5\n\n    for b_mask in shell_masks:\n        shelldata = data[..., b_mask]\n\n        a_thres = z_threshold * shelldata[brainmask].std() + shelldata[brainmask].mean()\n\n        spike_mask[..., b_mask] = shelldata > a_thres\n\n    return spike_mask\n\n\ndef noise_piesno(data: np.ndarray, n_channels: int = 4) -> (np.ndarray, np.ndarray):\n    \"\"\"\n    Estimates noise in raw diffusion MRI (dMRI) data using the PIESNO algorithm.\n\n    This function implements the PIESNO (Probabilistic Identification and Estimation\n    of Noise) algorithm [Koay2009]_ to estimate the standard deviation (sigma) of the\n    noise in each voxel of a 4D dMRI data array. The PIESNO algorithm assumes Rician\n    distributed signal and exploits the statistical properties of the noise to\n    separate it from the underlying signal.\n\n    Parameters\n    ----------\n    data : :obj:`~numpy.ndarray`\n        The 4D raw dMRI data array.\n    n_channels : :obj:`int`, optional (default=4)\n        The number of diffusion-encoding channels in the data. This value is used\n        internally by the PIESNO algorithm.\n\n    Returns\n    -------\n    sigma : :obj:`~numpy.ndarray`\n        The estimated noise standard deviation for each voxel in the data array.\n    mask : :obj:`~numpy.ndarray`\n        A brain mask estimated by PIESNO. This mask identifies voxels containing\n        mostly noise and can be used for further processing.\n\n    \"\"\"\n    from dipy.denoise.noise_estimate import piesno\n\n    sigma, mask = piesno(data, N=n_channels, return_mask=True)\n    return sigma, mask\n"
  },
  {
    "path": "mriqc/interfaces/functional.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nfrom __future__ import annotations\n\nfrom os import path as op\n\nimport nibabel as nb\nimport numpy as np\nimport pandas as pd\nfrom nipype.interfaces.base import (\n    BaseInterfaceInputSpec,\n    File,\n    InputMultiObject,\n    SimpleInterface,\n    TraitedSpec,\n    Undefined,\n    isdefined,\n    traits,\n)\nfrom nipype.utils.misc import normalize_mc_params\n\nfrom mriqc.qc.anatomical import efc, fber, snr, summary_stats\nfrom mriqc.qc.functional import gsr\nfrom mriqc.utils.misc import _flatten_dict\n\n\nclass FunctionalQCInputSpec(BaseInterfaceInputSpec):\n    in_epi = File(exists=True, mandatory=True, desc='input EPI file')\n    in_hmc = File(exists=True, mandatory=True, desc='input motion corrected file')\n    in_tsnr = File(exists=True, mandatory=True, desc='input tSNR volume')\n    in_mask = File(exists=True, mandatory=True, desc='input mask')\n    direction = traits.Enum(\n        'all',\n        'x',\n        'y',\n        '-x',\n        '-y',\n        usedefault=True,\n        desc='direction for GSR computation',\n    )\n    in_fd = File(\n        exists=True,\n        mandatory=True,\n        desc='motion parameters for FD computation',\n    )\n    fd_thres = traits.Float(0.2, usedefault=True, desc='motion threshold for FD computation')\n    in_dvars = File(exists=True, mandatory=True, desc='input file containing DVARS')\n    in_fwhm = traits.List(traits.Float, mandatory=True, desc='smoothness estimated with AFNI')\n\n\nclass FunctionalQCOutputSpec(TraitedSpec):\n    fber = traits.Float\n    efc = traits.Float\n    snr = traits.Float\n    gsr = traits.Dict\n    tsnr = traits.Float\n    dvars = traits.Dict\n    fd = traits.Dict\n    fwhm = traits.Dict(desc='full width half-maximum measure')\n    size = traits.Dict\n    spacing = traits.Dict\n    summary = traits.Dict\n\n    out_qc = traits.Dict(desc='output flattened dictionary with all measures')\n\n\nclass FunctionalQC(SimpleInterface):\n    \"\"\"\n    Computes anatomical :abbr:`QC (Quality Control)` measures on the\n    structural image given as input\n\n    \"\"\"\n\n    input_spec = FunctionalQCInputSpec\n    output_spec = FunctionalQCOutputSpec\n\n    def _run_interface(self, runtime):\n        # Get the mean EPI data and get it ready\n        epinii = nb.load(self.inputs.in_epi)\n        epidata = np.nan_to_num(np.float32(epinii.dataobj))\n        epidata[epidata < 0] = 0\n\n        # Get EPI data (with mc done) and get it ready\n        hmcnii = nb.load(self.inputs.in_hmc)\n        hmcdata = np.nan_to_num(np.float32(hmcnii.dataobj))\n        hmcdata[hmcdata < 0] = 0\n\n        # Get brain mask data\n        msknii = nb.load(self.inputs.in_mask)\n        mskdata = np.asanyarray(msknii.dataobj) > 0\n        if np.sum(mskdata) < 100:\n            raise RuntimeError(\n                'Detected less than 100 voxels belonging to the brain mask. '\n                'MRIQC failed to process this dataset.'\n            )\n\n        # Summary stats\n        rois = {'fg': mskdata.astype(np.uint8), 'bg': (~mskdata).astype(np.uint8)}\n        stats = summary_stats(epidata, rois)\n        self._results['summary'] = stats\n\n        # SNR\n        self._results['snr'] = snr(stats['fg']['median'], stats['fg']['stdv'], stats['fg']['n'])\n        # FBER\n        self._results['fber'] = fber(epidata, mskdata.astype(np.uint8))\n        # EFC\n        self._results['efc'] = efc(epidata)\n        # GSR\n        self._results['gsr'] = {}\n        if self.inputs.direction == 'all':\n            epidir = ['x', 'y']\n        else:\n            epidir = [self.inputs.direction]\n\n        for axis in epidir:\n            self._results['gsr'][axis] = gsr(epidata, mskdata.astype(np.uint8), direction=axis)\n\n        # DVARS\n        dvars_avg = np.loadtxt(self.inputs.in_dvars, skiprows=1, usecols=list(range(3))).mean(\n            axis=0\n        )\n        dvars_col = ['std', 'nstd', 'vstd']\n        self._results['dvars'] = {key: float(val) for key, val in zip(dvars_col, dvars_avg)}\n\n        # tSNR\n        tsnr_data = nb.load(self.inputs.in_tsnr).get_fdata()\n        self._results['tsnr'] = float(np.median(tsnr_data[mskdata]))\n\n        # FD\n        fd_data = np.loadtxt(self.inputs.in_fd, skiprows=1)\n        num_fd = (fd_data > self.inputs.fd_thres).sum()\n        self._results['fd'] = {\n            'mean': float(fd_data.mean()),\n            'num': int(num_fd),\n            'perc': float(num_fd * 100 / (len(fd_data) + 1)),\n        }\n\n        # FWHM\n        fwhm = np.array(self.inputs.in_fwhm[:3]) / np.array(hmcnii.header.get_zooms()[:3])\n        self._results['fwhm'] = {\n            'x': float(fwhm[0]),\n            'y': float(fwhm[1]),\n            'z': float(fwhm[2]),\n            'avg': float(np.average(fwhm)),\n        }\n\n        # Image specs\n        self._results['size'] = {\n            'x': int(hmcdata.shape[0]),\n            'y': int(hmcdata.shape[1]),\n            'z': int(hmcdata.shape[2]),\n        }\n        self._results['spacing'] = {\n            i: float(v) for i, v in zip(['x', 'y', 'z'], hmcnii.header.get_zooms()[:3])\n        }\n\n        try:\n            self._results['size']['t'] = int(hmcdata.shape[3])\n        except IndexError:\n            pass\n\n        try:\n            self._results['spacing']['tr'] = float(hmcnii.header.get_zooms()[3])\n        except IndexError:\n            pass\n\n        self._results['out_qc'] = _flatten_dict(self._results)\n        return runtime\n\n\nclass SpikesInputSpec(BaseInterfaceInputSpec):\n    in_file = File(exists=True, mandatory=True, desc='input fMRI dataset')\n    in_mask = File(exists=True, mandatory=True, desc='brain mask')\n    invert_mask = traits.Bool(False, usedefault=True, desc='invert mask')\n    no_zscore = traits.Bool(False, usedefault=True, desc='do not zscore')\n    detrend = traits.Bool(True, usedefault=True, desc='do detrend')\n    spike_thresh = traits.Float(\n        6.0,\n        usedefault=True,\n        desc='z-score to call one timepoint of one axial slice a spike',\n    )\n    skip_frames = traits.Int(\n        0,\n        usedefault=True,\n        desc='number of frames to skip in the beginning of the time series',\n    )\n    out_tsz = File('spikes_tsz.txt', usedefault=True, desc='output file name')\n    out_spikes = File('spikes_idx.txt', usedefault=True, desc='output file name')\n\n\nclass SpikesOutputSpec(TraitedSpec):\n    out_tsz = File(desc='slice-wise z-scored timeseries (Z x N), inside brainmask')\n    out_spikes = File(desc='indices of spikes')\n    num_spikes = traits.Int(desc='number of spikes found (total)')\n\n\nclass Spikes(SimpleInterface):\n    \"\"\"\n    Computes the number of spikes\n    https://github.com/cni/nims/blob/master/nimsproc/qa_report.py\n\n    \"\"\"\n\n    input_spec = SpikesInputSpec\n    output_spec = SpikesOutputSpec\n\n    def _run_interface(self, runtime):\n        func_nii = nb.load(self.inputs.in_file)\n        func_data = func_nii.get_fdata(dtype='float32')\n        func_shape = func_data.shape\n        ntsteps = func_shape[-1]\n        tr = func_nii.header.get_zooms()[-1]\n        nskip = self.inputs.skip_frames\n\n        mask_data = np.bool_(nb.load(self.inputs.in_mask).dataobj)\n        mask_data[..., :nskip] = 0\n        mask_data = np.stack([mask_data] * ntsteps, axis=-1)\n\n        if not self.inputs.invert_mask:\n            brain = np.ma.array(func_data, mask=(mask_data != 1))\n        else:\n            mask_data[..., : self.inputs.skip_frames] = 1\n            brain = np.ma.array(func_data, mask=(mask_data == 1))\n\n        if self.inputs.detrend:\n            from nilearn.signal import clean\n\n            brain = clean(brain[:, nskip:].T, t_r=tr, standardize=False).T\n\n        if self.inputs.no_zscore:\n            ts_z = find_peaks(brain)\n            total_spikes = []\n        else:\n            total_spikes, ts_z = find_spikes(brain, self.inputs.spike_thresh)\n        total_spikes = list(set(total_spikes))\n\n        out_tsz = op.abspath(self.inputs.out_tsz)\n        self._results['out_tsz'] = out_tsz\n        np.savetxt(out_tsz, ts_z)\n\n        out_spikes = op.abspath(self.inputs.out_spikes)\n        self._results['out_spikes'] = out_spikes\n        np.savetxt(out_spikes, total_spikes)\n        self._results['num_spikes'] = len(total_spikes)\n        return runtime\n\n\nclass _SelectEchoInputSpec(BaseInterfaceInputSpec):\n    in_files = InputMultiObject(File(exists=True), mandatory=True, desc='input EPI file(s)')\n    metadata = InputMultiObject(traits.Dict(), desc='sidecar JSON files corresponding to in_files')\n    te_reference = traits.Float(0.030, usedefault=True, desc='reference SE-EPI echo time')\n\n\nclass _SelectEchoOutputSpec(TraitedSpec):\n    out_file = File(desc='selected echo')\n    echo_index = traits.Int(desc='index of the selected echo')\n    is_multiecho = traits.Bool(desc='whether it is a multiecho dataset')\n\n\nclass SelectEcho(SimpleInterface):\n    \"\"\"\n    Computes anatomical :abbr:`QC (Quality Control)` measures on the\n    structural image given as input\n\n    \"\"\"\n\n    input_spec = _SelectEchoInputSpec\n    output_spec = _SelectEchoOutputSpec\n\n    def _run_interface(self, runtime):\n        (\n            self._results['out_file'],\n            self._results['echo_index'],\n        ) = select_echo(\n            self.inputs.in_files,\n            te_echos=(\n                _get_echotime(self.inputs.metadata) if isdefined(self.inputs.metadata) else None\n            ),\n            te_reference=self.inputs.te_reference,\n        )\n        self._results['is_multiecho'] = self._results['echo_index'] != -1\n        return runtime\n\n\nclass GatherTimeseriesInputSpec(TraitedSpec):\n    dvars = File(exists=True, mandatory=True, desc='file containing DVARS')\n    fd = File(exists=True, mandatory=True, desc='input framewise displacement')\n    mpars = File(exists=True, mandatory=True, desc='input motion parameters')\n    mpars_source = traits.Enum(\n        'FSL',\n        'AFNI',\n        'SPM',\n        'FSFAST',\n        'NIPY',\n        desc='Source of movement parameters',\n        mandatory=True,\n    )\n    outliers = File(\n        exists=True,\n        mandatory=True,\n        desc=\"input file containing timeseries of AFNI's outlier count\",\n    )\n    quality = File(exists=True, mandatory=True, desc=\"input file containing AFNI's Quality Index\")\n\n\nclass GatherTimeseriesOutputSpec(TraitedSpec):\n    timeseries_file = File(desc='output confounds file')\n    timeseries_metadata = traits.Dict(desc='Metadata dictionary describing columns')\n\n\nclass GatherTimeseries(SimpleInterface):\n    \"\"\"\n    Gather quality metrics that are timeseries into one TSV file\n\n    \"\"\"\n\n    input_spec = GatherTimeseriesInputSpec\n    output_spec = GatherTimeseriesOutputSpec\n\n    def _run_interface(self, runtime):\n        # motion parameters\n        mpars = np.apply_along_axis(\n            func1d=normalize_mc_params,\n            axis=1,\n            arr=np.loadtxt(self.inputs.mpars),  # mpars is N_t x 6\n            source=self.inputs.mpars_source,\n        )\n        timeseries = pd.DataFrame(\n            mpars, columns=['trans_x', 'trans_y', 'trans_z', 'rot_x', 'rot_y', 'rot_z']\n        )\n\n        # DVARS\n        dvars = pd.read_csv(\n            self.inputs.dvars,\n            sep=r'\\s+',\n            skiprows=1,  # column names have spaces\n            header=None,\n            names=['dvars_std', 'dvars_nstd', 'dvars_vstd'],\n        )\n        dvars.index = pd.RangeIndex(1, timeseries.index.max() + 1)\n\n        # FD\n        fd = pd.read_csv(self.inputs.fd, sep=r'\\s+', header=0, names=['framewise_displacement'])\n        fd.index = pd.RangeIndex(1, timeseries.index.max() + 1)\n\n        # AQI\n        aqi = pd.read_csv(self.inputs.quality, sep=r'\\s+', header=None, names=['aqi'])\n\n        # Outliers\n        aor = pd.read_csv(self.inputs.outliers, sep=r'\\s+', header=None, names=['aor'])\n\n        timeseries = pd.concat((timeseries, dvars, fd, aqi, aor), axis=1)\n\n        timeseries_file = op.join(runtime.cwd, 'timeseries.tsv')\n\n        timeseries.to_csv(timeseries_file, sep='\\t', index=False, na_rep='n/a')\n\n        self._results['timeseries_file'] = timeseries_file\n        self._results['timeseries_metadata'] = _build_timeseries_metadata()\n        return runtime\n\n\ndef _build_timeseries_metadata():\n    return {\n        'trans_x': {\n            'LongName': 'Translation Along X Axis',\n            'Description': 'Estimated Motion Parameter',\n            'Units': 'mm',\n        },\n        'trans_y': {\n            'LongName': 'Translation Along Y Axis',\n            'Description': 'Estimated Motion Parameter',\n            'Units': 'mm',\n        },\n        'trans_z': {\n            'LongName': 'Translation Along Z Axis',\n            'Description': 'Estimated Motion Parameter',\n            'Units': 'mm',\n        },\n        'rot_x': {\n            'LongName': 'Rotation Around X Axis',\n            'Description': 'Estimated Motion Parameter',\n            'Units': 'rad',\n        },\n        'rot_y': {\n            'LongName': 'Rotation Around X Axis',\n            'Description': 'Estimated Motion Parameter',\n            'Units': 'rad',\n        },\n        'rot_z': {\n            'LongName': 'Rotation Around X Axis',\n            'Description': 'Estimated Motion Parameter',\n            'Units': 'rad',\n        },\n        'dvars_std': {\n            'LongName': 'Derivative of RMS Variance over Voxels, Standardized',\n            'Description': (\n                'Indexes the rate of change of BOLD signal across'\n                'the entire brain at each frame of data, normalized with the'\n                'standard deviation of the temporal difference time series'\n            ),\n        },\n        'dvars_nstd': {\n            'LongName': ('Derivative of RMS Variance over Voxels, Non-Standardized'),\n            'Description': (\n                'Indexes the rate of change of BOLD signal across'\n                'the entire brain at each frame of data, not normalized.'\n            ),\n        },\n        'dvars_vstd': {\n            'LongName': 'Derivative of RMS Variance over Voxels, Standardized',\n            'Description': (\n                'Indexes the rate of change of BOLD signal across'\n                'the entire brain at each frame of data, normalized across'\n                'time by that voxel standard deviation across time,'\n                'before computing the RMS of the temporal difference'\n            ),\n        },\n        'framewise_displacement': {\n            'LongName': 'Framewise Displacement',\n            'Description': (\n                'A quantification of the estimated bulk-head'\n                'motion calculated using formula proposed by Power (2012)'\n            ),\n            'Units': 'mm',\n        },\n        'aqi': {\n            'LongName': \"AFNI's Quality Index\",\n            'Description': \"Mean quality index as computed by AFNI's 3dTqual\",\n        },\n        'aor': {\n            'LongName': \"AFNI's Fraction of Outliers per Volume\",\n            'Description': (\n                \"Mean fraction of outliers per fMRI volume as given by AFNI's 3dToutcount\"\n            ),\n        },\n    }\n\n\ndef find_peaks(data):\n    t_z = [data[:, :, i, :].mean(axis=0).mean(axis=0) for i in range(data.shape[2])]\n    return t_z\n\n\ndef find_spikes(data, spike_thresh):\n    data -= np.median(np.median(np.median(data, axis=0), axis=0), axis=0)\n    slice_mean = np.median(np.median(data, axis=0), axis=0)\n    t_z = _robust_zscore(slice_mean)\n    spikes = np.abs(t_z) > spike_thresh\n    spike_inds = np.transpose(spikes.nonzero())\n    # mask out the spikes and recompute z-scores using variance uncontaminated with spikes.\n    # This will catch smaller spikes that may have been swamped by big\n    # ones.\n    data.mask[:, :, spike_inds[:, 0], spike_inds[:, 1]] = True\n    slice_mean2 = np.median(np.median(data, axis=0), axis=0)\n    t_z = _robust_zscore(slice_mean2)\n\n    spikes = np.logical_or(spikes, np.abs(t_z) > spike_thresh)\n    spike_inds = [tuple(i) for i in np.transpose(spikes.nonzero())]\n    return spike_inds, t_z\n\n\ndef _robust_zscore(data):\n    return (data - np.atleast_2d(np.median(data, axis=1)).T) / np.atleast_2d(data.std(axis=1)).T\n\n\ndef select_echo(\n    in_files: str | list[str],\n    te_echos: list[float | Undefined | None] | None = None,\n    te_reference: float = 0.030,\n) -> str:\n    \"\"\"\n    Select the echo file with the closest echo time to the reference echo time.\n\n    Used to grab the echo file when processing multi-echo data through workflows\n    that only accept a single file.\n\n    Parameters\n    ----------\n    in_files : :obj:`str` or :obj:`list`\n        A single filename or a list of filenames.\n    te_echos : :obj:`list` of :obj:`float`\n        List of echo times corresponding to each file.\n        If not a number (typically, a :obj:`~nipype.interfaces.base.Undefined`),\n        the function selects the second echo.\n    te_reference : float, optional\n        Reference echo time used to find the closest echo time.\n\n    Returns\n    -------\n    str\n        The selected echo file.\n\n    Examples\n    --------\n    >>> select_echo(\"single-echo.nii.gz\")\n    ('single-echo.nii.gz', -1)\n\n    >>> select_echo([\"single-echo.nii.gz\"])\n    ('single-echo.nii.gz', -1)\n\n    >>> select_echo(\n    ...     [f\"echo{n}.nii.gz\" for n in range(1,7)],\n    ... )\n    ('echo2.nii.gz', 1)\n\n    >>> select_echo(\n    ...     [f\"echo{n}.nii.gz\" for n in range(1,7)],\n    ...     te_echos=[12.5, 28.5, 34.2, 45.0, 56.1, 68.4],\n    ...     te_reference=33.1,\n    ... )\n    ('echo3.nii.gz', 2)\n\n    >>> select_echo(\n    ...     [f\"echo{n}.nii.gz\" for n in range(1,7)],\n    ...     te_echos=[12.5, 28.5, 34.2, 45.0, 56.1],\n    ...     te_reference=33.1,\n    ... )\n    ('echo2.nii.gz', 1)\n\n    >>> select_echo(\n    ...     [f\"echo{n}.nii.gz\" for n in range(1,7)],\n    ...     te_echos=[12.5, 28.5, 34.2, 45.0, 56.1, None],\n    ...     te_reference=33.1,\n    ... )\n    ('echo2.nii.gz', 1)\n\n    \"\"\"\n    if not isinstance(in_files, (list, tuple)):\n        return in_files, -1\n\n    if len(in_files) == 1:\n        return in_files[0], -1\n\n    import numpy as np\n\n    n_echos = len(in_files)\n    if te_echos is not None and len(te_echos) == n_echos:\n        try:\n            index = np.argmin(np.abs(np.array(te_echos) - te_reference))\n            return in_files[index], index\n        except TypeError:\n            pass\n\n    return in_files[1], 1\n\n\ndef _get_echotime(inlist):\n    if isinstance(inlist, list):\n        retval = [_get_echotime(el) for el in inlist]\n        return retval[0] if len(retval) == 1 else retval\n\n    echo_time = inlist.get('EchoTime', None) if inlist else None\n\n    if echo_time:\n        return float(echo_time)\n"
  },
  {
    "path": "mriqc/interfaces/reports.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Reports.\"\"\"\n\nimport nibabel as nb\nimport numpy as np\nfrom nipype.interfaces.base import (\n    BaseInterfaceInputSpec,\n    File,\n    SimpleInterface,\n    TraitedSpec,\n    traits,\n)\n\nfrom mriqc import config\n\n\nclass _AddProvenanceInputSpec(BaseInterfaceInputSpec):\n    in_file = File(exists=True, desc='input file')\n    air_msk = File(exists=True, desc='air mask file')\n    rot_msk = File(exists=True, desc='rotation mask file')\n    modality = traits.Str(mandatory=True, desc='provenance type')\n\n\nclass _AddProvenanceOutputSpec(TraitedSpec):\n    out_prov = traits.Dict()\n\n\nclass AddProvenance(SimpleInterface):\n    \"\"\"Builds a provenance dictionary.\"\"\"\n\n    input_spec = _AddProvenanceInputSpec\n    output_spec = _AddProvenanceOutputSpec\n\n    def _run_interface(self, runtime):\n        from nipype.utils.filemanip import hash_infile\n\n        self._results['out_prov'] = {\n            'md5sum': hash_infile(self.inputs.in_file),\n            'version': config.environment.version,\n            'software': 'mriqc',\n            'settings': {\n                'testing': config.execution.debug,\n            },\n        }\n\n        if self.inputs.modality in ('T1w', 'T2w'):\n            air_msk_size = np.asanyarray(nb.load(self.inputs.air_msk).dataobj).astype(bool).sum()\n            rot_msk_size = np.asanyarray(nb.load(self.inputs.rot_msk).dataobj).astype(bool).sum()\n            self._results['out_prov']['warnings'] = {\n                'small_air_mask': bool(air_msk_size < 5e5),\n                'large_rot_frame': bool(rot_msk_size > 500),\n            }\n\n        if self.inputs.modality == 'bold':\n            self._results['out_prov']['settings'].update(\n                {\n                    'fd_thres': config.workflow.fd_thres,\n                }\n            )\n\n        return runtime\n"
  },
  {
    "path": "mriqc/interfaces/synthstrip.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2022 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"SynthStrip interface.\"\"\"\n\nfrom nipype.interfaces.base import (\n    CommandLine,\n    CommandLineInputSpec,\n    File,\n    TraitedSpec,\n    Undefined,\n    traits,\n)\n\nfrom mriqc import config\n\n_model_path = (\n    str(config.environment.synthstrip_path)\n    if config.environment.synthstrip_path is not None\n    else Undefined\n)\n\n\nclass _SynthStripInputSpec(CommandLineInputSpec):\n    in_file = File(\n        exists=True,\n        mandatory=True,\n        argstr='-i %s',\n        desc='Input image to be brain extracted',\n    )\n    use_gpu = traits.Bool(False, usedefault=True, argstr='-g', desc='Use GPU', nohash=True)\n    model = File(\n        _model_path,\n        usedefault=True,\n        exists=True,\n        argstr='--model %s',\n        desc=\"file containing model's weights\",\n    )\n    border_mm = traits.Int(1, usedefault=True, argstr='-b %d', desc='Mask border threshold in mm')\n    out_file = File(\n        name_source=['in_file'],\n        name_template='%s_desc-brain.nii.gz',\n        argstr='-o %s',\n        desc='store brain-extracted input to file',\n    )\n    out_mask = File(\n        name_source=['in_file'],\n        name_template='%s_desc-brain_mask.nii.gz',\n        argstr='-m %s',\n        desc='store brainmask to file',\n    )\n    num_threads = traits.Int(desc='Number of threads', argstr='-n %d', nohash=True)\n\n\nclass _SynthStripOutputSpec(TraitedSpec):\n    out_file = File(desc='brain-extracted image')\n    out_mask = File(desc='brain mask')\n\n\nclass SynthStrip(CommandLine):\n    _cmd = 'synthstrip'\n    input_spec = _SynthStripInputSpec\n    output_spec = _SynthStripOutputSpec\n"
  },
  {
    "path": "mriqc/interfaces/tests/test_interfaces.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nAnatomical tests\n\"\"\"\n# import os.path as op\n# import numpy as np\n# import pytest\n# from scipy.stats import rice\n# from mriqc.interfaces.anatomical import artifact_mask\n\n# @pytest.mark.parametrize(\"sigma\", [0.02, 0.03, 0.05, 0.08, 0.12, 0.15, 0.2, 0.4, 0.5])\n# def test_qi1(sigma):\n#     size = (50, 50, 50)\n#     test_data = np.ones(size)\n#     wmdata = np.zeros(size)\n#     bgdata = np.ones(size)\n#     wmdata[22:24, 22:24, 22:24] = 1\n#     wm_mean = 100\n#     test_data[wmdata > 0] = wm_mean\n#     test_data += rice.rvs(0.77, scale=sigma*wm_mean, size=test_data.shape)\n#     artmask = artifact_mask(test_data, bgdata, bgdata, zscore=2.)\n#     qi1 = artmask.sum() / bgdata.sum()\n#     assert qi1 > .0 and qi1 < 0.002\n"
  },
  {
    "path": "mriqc/interfaces/transitional.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nfrom nipype.interfaces.base import (\n    CommandLine,\n    CommandLineInputSpec,\n    File,\n    TraitedSpec,\n    traits,\n)\n\n\nclass GCORInputSpec(CommandLineInputSpec):\n    in_file = File(\n        desc='input dataset to compute the GCOR over',\n        argstr='-input %s',\n        position=-1,\n        mandatory=True,\n        exists=True,\n        copyfile=False,\n    )\n\n    mask = File(\n        desc='mask dataset, for restricting the computation',\n        argstr='-mask %s',\n        exists=True,\n        copyfile=False,\n    )\n\n    nfirst = traits.Int(0, argstr='-nfirst %d', desc='specify number of initial TRs to ignore')\n    no_demean = traits.Bool(\n        False,\n        argstr='-no_demean',\n        desc='do not (need to) demean as first step',\n    )\n\n\nclass GCOROutputSpec(TraitedSpec):\n    out = traits.Float(desc='global correlation value')\n\n\nclass GCOR(CommandLine):\n    \"\"\"\n    Computes the average correlation between every voxel\n    and ever other voxel, over any give mask.\n    For complete details, see the `@compute_gcor Documentation.\n    <https://afni.nimh.nih.gov/pub/dist/doc/program_help/@compute_gcor.html>`_\n\n    Examples\n    ========\n    >>> from mriqc.interfaces.transitional import GCOR\n    >>> gcor = GCOR()\n    >>> gcor.inputs.in_file = 'func.nii'\n    >>> gcor.inputs.nfirst = 4\n    >>> gcor.cmdline  # doctest: +ALLOW_UNICODE\n    '@compute_gcor -nfirst 4 -input func.nii'\n    >>> res = gcor.run()  # doctest: +SKIP\n\n    \"\"\"\n\n    _cmd = '@compute_gcor'\n    input_spec = GCORInputSpec\n    output_spec = GCOROutputSpec\n\n    def _run_interface(self, runtime):\n        runtime = super()._run_interface(runtime)\n\n        gcor_line = [\n            line.strip()\n            for line in runtime.stdout.split('\\n')\n            if line.strip().startswith('GCOR = ')\n        ][-1]\n        str_len = len('GCOR = ')\n        self._gcor = float(gcor_line[str_len:])\n        return runtime\n\n    def _list_outputs(self):\n        return {'out': self._gcor}\n"
  },
  {
    "path": "mriqc/interfaces/webapi.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nfrom pathlib import Path\n\nimport orjson\nfrom nipype.interfaces.base import (\n    BaseInterfaceInputSpec,\n    Bunch,\n    File,\n    SimpleInterface,\n    Str,\n    TraitedSpec,\n    isdefined,\n    traits,\n)\n\nfrom mriqc import config, messages\n\n# metadata whitelist\nMETA_WHITELIST = [\n    'AccelNumReferenceLines',\n    'AccelerationFactorPE',\n    'AcquisitionMatrix',\n    'CogAtlasID',\n    'CogPOID',\n    'CoilCombinationMethod',\n    'ContrastBolusIngredient',\n    'ConversionSoftware',\n    'ConversionSoftwareVersion',\n    'DelayTime',\n    'DeviceSerialNumber',\n    'EchoTime',\n    'EchoTrainLength',\n    'EffectiveEchoSpacing',\n    'FlipAngle',\n    'GradientSetType',\n    'HardcopyDeviceSoftwareVersion',\n    'ImageType',\n    'ImagingFrequency',\n    'InPlanePhaseEncodingDirection',\n    'InstitutionAddress',\n    'InstitutionName',\n    'Instructions',\n    'InversionTime',\n    'MRAcquisitionType',\n    'MRTransmitCoilSequence',\n    'MagneticFieldStrength',\n    'Manufacturer',\n    'ManufacturersModelName',\n    'MatrixCoilMode',\n    'MultibandAccelerationFactor',\n    'NumberOfAverages',\n    'NumberOfPhaseEncodingSteps',\n    'NumberOfVolumesDiscardedByScanner',\n    'NumberOfVolumesDiscardedByUser',\n    'NumberShots',\n    'ParallelAcquisitionTechnique',\n    'ParallelReductionFactorInPlane',\n    'PartialFourier',\n    'PartialFourierDirection',\n    'PatientPosition',\n    'PercentPhaseFieldOfView',\n    'PercentSampling',\n    'PhaseEncodingDirection',\n    'PixelBandwidth',\n    'ProtocolName',\n    'PulseSequenceDetails',\n    'PulseSequenceType',\n    'ReceiveCoilName',\n    'RepetitionTime',\n    'ScanOptions',\n    'ScanningSequence',\n    'SequenceName',\n    'SequenceVariant',\n    'SliceEncodingDirection',\n    'SoftwareVersions',\n    'TaskDescription',\n    'TaskName',\n    'TotalReadoutTime',\n    'TotalScanTimeSec',\n    'TransmitCoilName',\n    'VariableFlipAngleFlag',\n    'acq_id',\n    'modality',\n    'run_id',\n    'subject_id',\n    'task_id',\n    'session_id',\n]\n\nPROV_WHITELIST = ['version', 'md5sum', 'software', 'settings']\n\nHASH_BIDS = ['subject_id', 'session_id']\n\n\nclass UploadIQMsInputSpec(BaseInterfaceInputSpec):\n    in_iqms = File(exists=True, mandatory=True, desc='the input IQMs-JSON file')\n    endpoint = Str(mandatory=True, desc='URL of the POST endpoint')\n    auth_token = Str(mandatory=True, desc='authentication token')\n    email = Str(desc='set sender email')\n    strict = traits.Bool(False, usedefault=True, desc='crash if upload was not successful')\n    modality = Str(\n        'undefined',\n        usedefault=True,\n        desc='override modality field if provided through metadata',\n    )\n\n\nclass UploadIQMsOutputSpec(TraitedSpec):\n    api_id = traits.Either(None, traits.Str, desc='Id for report returned by the web api')\n    payload_file = File(desc='Submitted payload (only for debugging)')\n\n\nclass UploadIQMs(SimpleInterface):\n    \"\"\"\n    Upload features to MRIQCWebAPI\n    \"\"\"\n\n    input_spec = UploadIQMsInputSpec\n    output_spec = UploadIQMsOutputSpec\n    always_run = True\n\n    def _run_interface(self, runtime):\n        email = None\n        if isdefined(self.inputs.email):\n            email = self.inputs.email\n\n        self._results['api_id'] = None\n\n        response, payload = upload_qc_metrics(\n            self.inputs.in_iqms,\n            endpoint=self.inputs.endpoint,\n            auth_token=self.inputs.auth_token,\n            email=email,\n            modality=self.inputs.modality,\n        )\n\n        payload_str = orjson.dumps(\n            payload,\n            option=(\n                orjson.OPT_SORT_KEYS\n                | orjson.OPT_INDENT_2\n                | orjson.OPT_APPEND_NEWLINE\n                | orjson.OPT_SERIALIZE_NUMPY\n            ),\n        ).decode('utf-8')\n        Path('payload.json').write_text(payload_str)\n        self._results['payload_file'] = str(Path('payload.json').absolute())\n\n        try:\n            self._results['api_id'] = response.json()['_id']\n        except (AttributeError, KeyError, ValueError):\n            # response did not give us an ID\n            errmsg = (\n                'QC metrics upload failed to create an ID for the record '\n                f'uploaded. Response from server follows: {response.text}'\n                '\\n\\nPayload:\\n'\n                f'{payload_str}'\n            )\n            config.loggers.interface.warning(errmsg)\n\n        if response.status_code == 201:\n            config.loggers.interface.info(messages.QC_UPLOAD_COMPLETE)\n            return runtime\n\n        errmsg = '\\n'.join(\n            [\n                'Unsuccessful upload.',\n                f'Server response status {response.status_code}:',\n                response.text,\n                '',\n                '',\n                'Payload:',\n                f'{payload_str}',\n            ]\n        )\n        config.loggers.interface.warning(errmsg)\n        if self.inputs.strict:\n            raise RuntimeError(errmsg)\n\n        return runtime\n\n\ndef upload_qc_metrics(\n    in_iqms,\n    endpoint=None,\n    email=None,\n    auth_token=None,\n    modality=None,\n):\n    \"\"\"\n    Upload qc metrics to remote repository.\n\n    :param str in_iqms: Path to the qc metric json file as a string\n    :param str webapi_url: the protocol (either http or https)\n    :param str email: email address to be included with the metric submission\n    :param str auth_token: authentication token\n\n    :return: either the response object if a response was successfully sent\n             or it returns the string \"No Response\"\n    :rtype: object\n\n\n    \"\"\"\n    from copy import deepcopy\n\n    import requests\n\n    if not endpoint or not auth_token:\n        # If endpoint unknown, do not even report what happens to the token.\n        errmsg = 'Unknown API endpoint' if not endpoint else 'Authentication failed.'\n        return Bunch(status_code=1, text=errmsg)\n\n    in_data = orjson.loads(Path(in_iqms).read_bytes())\n\n    # Extract metadata and provenance\n    meta = in_data.pop('bids_meta')\n    prov = in_data.pop('provenance')\n\n    # At this point, data should contain only IQMs\n    data = deepcopy(in_data)\n\n    # Check modality\n    modality = meta.get('modality', None) or meta.get('suffix', None) or modality\n    if modality not in ('T1w', 'bold', 'T2w'):\n        errmsg = (\n            'Submitting to MRIQCWebAPI: image modality should be \"bold\", \"T1w\", or \"T2w\", '\n            f'(found \"{modality}\")'\n        )\n        return Bunch(status_code=1, text=errmsg)\n\n    # Filter metadata values that aren't in whitelist\n    data['bids_meta'] = {k: meta[k] for k in META_WHITELIST if k in meta}\n\n    # Check for fields with appended _id\n    bids_meta_names = {k: k.replace('_id', '') for k in META_WHITELIST if k.endswith('_id')}\n    data['bids_meta'].update({k: meta[v] for k, v in bids_meta_names.items() if v in meta})\n\n    # For compatibility with WebAPI. Should be rolled back to int\n    if (run_id := data['bids_meta'].get('run_id', None)) is not None:\n        data['bids_meta']['run_id'] = f'{run_id}'\n\n    # One more chance for spelled-out BIDS entity acquisition\n    if (acq_id := meta.get('acquisition', None)) is not None:\n        data['bids_meta']['acq_id'] = acq_id\n\n    # Filter provenance values that aren't in whitelist\n    data['provenance'] = {k: prov[k] for k in PROV_WHITELIST if k in prov}\n\n    # Hash fields that may contain personal information\n    data['bids_meta'] = _hashfields(data['bids_meta'])\n\n    data['bids_meta']['modality'] = modality\n\n    if email:\n        data['provenance']['email'] = email\n\n    headers = {'Authorization': auth_token, 'Content-Type': 'application/json'}\n\n    start_message = messages.QC_UPLOAD_START.format(url=endpoint)\n    config.loggers.interface.info(start_message)\n\n    errmsg = None\n    try:\n        # if the modality is bold, call \"bold\" endpoint\n        response = requests.post(\n            f'{endpoint}/{modality}',\n            headers=headers,\n            data=orjson.dumps(data, option=orjson.OPT_SERIALIZE_NUMPY),\n            timeout=15,\n        )\n    except requests.ConnectionError as err:\n        errmsg = ('Error uploading IQMs: Connection error:', f'{err}')\n    except requests.exceptions.ReadTimeout as err:\n        errmsg = (f'Error uploading IQMs: Server {endpoint} is down.', f'{err}')\n\n    if errmsg is not None:\n        response = Bunch(status_code=1, text='\\n'.join(errmsg))\n\n    return response, data\n\n\ndef _hashfields(data):\n    from hashlib import sha256\n\n    for name in HASH_BIDS:\n        if name in data:\n            data[name] = sha256(data[name].encode()).hexdigest()\n\n    return data\n"
  },
  {
    "path": "mriqc/messages.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Logging and console messages.\"\"\"\n\nBIDS_META = 'Generating BIDS derivatives metadata.'\nBUILDING_WORKFLOW = 'Building {modality} MRIQC workflow {detail}.'\nCREATED_DATASET = 'Created dataset X=\"{feat_file}\", Y=\"{label_file}\" (N={n_samples} valid samples)'\nDROPPING_NON_NUMERICAL = 'Dropping {n_labels} samples for having non-numerical labels'\nGROUP_FINISHED = 'Group level finished successfully.'\nGROUP_NO_DATA = 'No data found. No group level reports were generated.'\nGROUP_START = \"\"\"\\\n------------------------------------------------------------------\nGenerating group reports\n------------------------------------------------------------------\n\"\"\"\nINDIVIDUAL_REPORT_GENERATED = 'Generated individual log: {out_file}'\nPARTICIPANT_START = \"\"\"\\\n------------------------------------------------------------------\n  Running MRIQC version {version}\n  ----------------------------------------------------------------\n\n  {notice}\n\n  ----------------------------------------------------------------\n\n  * BIDS dataset path: {bids_dir}.\n  * Output folder: {output_dir}.\n  * Analysis levels: {analysis_level}.\n{extra_messages}------------------------------------------------------------------\n\"\"\"\nPARTICIPANT_FINISHED = \"\"\"\\\nParticipant level finished successfully averaging {duration} per subject.\"\"\"\nPOST_Z_NANS = 'Columns {nan_columns} contain NaNs after z-scoring.'\nQC_UPLOAD_COMPLETE = 'QC metrics successfully uploaded.'\nQC_UPLOAD_START = 'MRIQC Web API: submitting to <{url}>'\nGROUP_REPORT_GENERATED = 'Group-{modality} report generated: {path}'\nRUN_FINISHED = \"\"\"\\\n----------------------------------------------------------------\n  MRIQC completed (elapsed time {duration}).\n----------------------------------------------------------------\n\"\"\"\nSUSPICIOUS_DATA_TYPE = \"Input image {in_file} has a suspicious data type: '{dtype}'\"\nTSV_GENERATED = 'Generated summary TSV table for {modality} data: {path}'\nVOXEL_SIZE_OK = 'Voxel size is large enough.'\nVOXEL_SIZE_SMALL = (\n    'One or more voxel dimensions (%f, %f, %f) are smaller than the '\n    'requested voxel size (%f) - diff=(%f, %f, %f)'\n)\nZ_SCORING = 'z-scoring dataset...'\n"
  },
  {
    "path": "mriqc/qc/__init__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nSome no-reference :abbr:`IQMs (image quality metrics)` are extracted in the\nfinal stage of all processing workflows run by MRIQC.\nA no-reference :abbr:`IQM (image quality metric)` is a measurement of some aspect\nof the actual image which cannot be compared to a reference value for the metric\nsince there is no ground-truth about what this number should be.\nAll the computed :abbr:`IQMs (image quality metrics)` corresponding to\nan image are saved in a `JSON file <iqm_json>`_ under the ``<output-dir>/derivatives/``\nfolder.\n\nThe IQMs can be grouped in four broad categories, providing a vector of 56\nfeatures per anatomical image. Some measures characterize the impact of noise and/or\nevaluate the fitness of a noise model. A second family of measures use information\ntheory and prescribed masks to evaluate the spatial distribution of information. A third\nfamily of measures look for the presence and impact of particular artifacts. Specifically,\nthe INU artifact, and the signal leakage due to rapid motion (e.g. eyes motion or blood\nvessel pulsation) are identified. Finally, some measures that do not fit within the\nprevious categories characterize the statistical properties of tissue distributions, volume\noverlap of tissues with respect to the volumes projected from MNI space, the\nsharpness/blurriness of the images, etc.\n\n.. note ::\n\n  Most of the :abbr:`IQMs (image quality metrics)` in this module are adapted, derived or\n  reproduced from the :abbr:`QAP (quality assessment protocols)` project [QAP]_.\n  We particularly thank Steve Giavasis (`@sgiavasis <https://github.com/sgiavasis>`_) and\n  Krishna Somandepali for their original implementations of the code in this module that\n  we took from the [QAP]_.\n  The [QAP]_ has a very good description of the :abbr:`IQMs (image quality metrics)`\n  in [QAP-measures]_.\n\n\n.. toctree::\n    :maxdepth: 3\n\n    iqms/t1w\n    iqms/bold\n    iqms/dwi\n\n\n\n.. topic:: References\n\n  .. [QAP] `The QAP project\n    <https://github.com/oesteban/quality-assessment-protocol/blob/enh/SmartQCWorkflow/qap/temporal_qc.py#L16>`_.\n\n  .. [QAP-measures] `The Quality Assessment Protocols website: Taxonomy of QA Measures\n    <http://preprocessed-connectomes-project.org/quality-assessment-protocol/#taxonomy-of-qa-measures>`_.\n\n\n\"\"\"\n"
  },
  {
    "path": "mriqc/qc/anatomical.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nr\"\"\"\nMeasures based on noise measurements\n====================================\n\n.. _iqms_cjv:\n\n- :py:func:`~mriqc.qc.anatomical.cjv` -- **coefficient of joint variation**\n  (:abbr:`CJV (coefficient of joint variation)`):\n  The ``cjv`` of GM and WM was proposed as objective function by [Ganzetti2016]_ for\n  the optimization of :abbr:`INU (intensity non-uniformity)` correction algorithms.\n  Higher values are related to the presence of heavy head motion and large\n  :abbr:`INU (intensity non-uniformity)` artifacts. Lower values are better.\n\n.. _iqms_cnr:\n\n- :py:func:`~mriqc.qc.anatomical.cnr` -- **contrast-to-noise ratio**\n  (:abbr:`CNR (contrast-to-noise ratio)`): The ``cnr`` [Magnota2006]_,\n  is an extension of the :abbr:`SNR (signal-to-noise Ratio)` calculation\n  to evaluate how separated the tissue distributions of GM and WM are.\n  Higher values indicate better quality.\n\n.. _iqms_snr:\n\n- :py:func:`~mriqc.qc.anatomical.snr` -- **signal-to-noise ratio**\n  (:abbr:`SNR (signal-to-noise ratio)`): calculated within the\n  tissue mask.\n\n.. _iqms_snrd:\n\n- :py:func:`~mriqc.qc.anatomical.snr_dietrich`: **Dietrich's SNR**\n  (:abbr:`SNRd (signal-to-noise ratio, Dietrich 2007)`) as proposed\n  by [Dietrich2007]_, using the air background as reference.\n\n.. _iqms_qi2:\n\n- :py:func:`~mriqc.qc.anatomical.art_qi2`: **Mortamet's quality index 2**\n  (:abbr:`QI2 (quality index 2)`) is a calculation of the goodness-of-fit\n  of a :math:`\\chi^2` distribution on the air mask,\n  once the artifactual intensities detected for computing\n  the :abbr:`QI1 (quality index 1)` index have been removed [Mortamet2009]_.\n  Lower values are better.\n\nMeasures based on information theory\n------------------------------------\n\n.. _iqms_efc:\n\n- :py:func:`~mriqc.qc.anatomical.efc`:\n  The :abbr:`EFC (Entropy Focus Criterion)`\n  [Atkinson1997]_ uses the Shannon entropy of voxel intensities as\n  an indication of ghosting and blurring induced by head motion.\n  Lower values are better.\n\n  The original equation is normalized by the maximum entropy, so that the\n  :abbr:`EFC (Entropy Focus Criterion)` can be compared across images with\n  different dimensions.\n\n.. _iqms_fber:\n\n- :py:func:`~mriqc.qc.anatomical.fber`:\n  The :abbr:`FBER (Foreground-Background Energy Ratio)` [Shehzad2015]_,\n  defined as the mean energy of image values within the head relative\n  to outside the head [QAP-measures]_.\n  Higher values are better.\n\nMeasures targeting specific artifacts\n-------------------------------------\n\n.. _iqms_inu:\n\n- **inu_\\*** (*nipype interface to N4ITK*): summary statistics (max, min and median)\n  of the :abbr:`INU (intensity non-uniformity)` field (bias field) as extracted\n  by the N4ITK algorithm [Tustison2010]_. Values closer to 1.0 are better, values\n  further from zero indicate greater RF field inhomogeneity.\n\n.. _iqms_qi:\n\n- :py:func:`~mriqc.qc.anatomical.art_qi1`:\n  Detect artifacts in the image using the method described in [Mortamet2009]_.\n  The :abbr:`QI1 (quality index 1)` is the proportion of voxels with intensity\n  corrupted by artifacts normalized by the number of voxels in the background.\n  Lower values are better.\n\n  .. figure:: ../resources/mortamet-mrm2009.png\n\n    The workflow to compute the artifact detection from [Mortamet2009]_.\n\n.. _iqms_wm2max:\n\n- :py:func:`~mriqc.qc.anatomical.wm2max`:\n  The white-matter to maximum intensity ratio is the median intensity\n  within the WM mask over the 95% percentile of the full intensity\n  distribution, that captures the existence of long tails due to\n  hyper-intensity of the carotid vessels and fat. Values\n  should be around the interval [0.6, 0.8].\n\n\nOther measures\n^^^^^^^^^^^^^^\n\n.. _iqms_fwhm:\n\n- **fwhm** (*nipype interface to AFNI*): The :abbr:`FWHM (full-width half maximum)` of\n  the spatial distribution of the image intensity values in units of voxels [Forman1995]_.\n  Lower values are better, higher values indicate a blurrier image. Uses the gaussian\n  width estimator filter implemented in AFNI's ``3dFWHMx``:\n\n  .. math ::\n\n      \\text{FWHM} = \\sqrt{-{\\left[4 \\ln{(1-\\frac{\\sigma^2_{X^m_{i+1,j}-X^m_{i,j}}}\n      {2\\sigma^2_{X^m_{i,j}}}})\\right]}^{-1}}\n\n\n.. _iqms_icvs:\n\n- :py:func:`~mriqc.qc.anatomical.volume_fraction` (**icvs_\\***):\n  the\n  :abbr:`ICV (intracranial volume)` fractions of :abbr:`CSF (cerebrospinal fluid)`,\n  :abbr:`GM (gray-matter)` and :abbr:`WM (white-matter)`. They should move within\n  a normative range.\n\n.. _iqms_rpve:\n\n- :py:func:`~mriqc.qc.anatomical.rpve` (**rpve_\\***): the\n  :abbr:`rPVe (residual partial voluming error)` of :abbr:`CSF (cerebrospinal fluid)`,\n  :abbr:`GM (gray-matter)` and :abbr:`WM (white-matter)`. Lower values are better.\n\n.. _iqms_summary:\n\n- :py:func:`~mriqc.qc.anatomical.summary_stats` (**summary_\\*_\\***):\n  Mean, median, median absolute deviation (mad), standard deviation, kurtosis,\n  5% percentile, 95% percentile and number of voxels of the distribution of background (bg),\n  foreground (fg: corresponds to the voxels within the brain mask),\n  :abbr:`CSF (cerebrospinal fluid)`, :abbr:`GM (gray-matter)` and :abbr:`WM (white-matter)`.\n\n.. _iqms_tpm:\n\n- **overlap_\\*_\\***:\n  The overlap of the :abbr:`TPMs (tissue probability maps)` estimated from the image and\n  the corresponding maps from the ICBM nonlinear-asymmetric 2009c template. Higher\n  values are better.\n\n  .. math ::\n\n      \\text{JI}^k = \\frac{\\sum_i \\min{(\\text{TPM}^k_i, \\text{MNI}^k_i)}}\n      {\\sum_i \\max{(\\text{TPM}^k_i, \\text{MNI}^k_i)}}\n\n\n.. topic:: References\n\n  .. [Dietrich2007] Dietrich et al., *Measurement of SNRs in MR images: influence\n    of multichannel coils, parallel imaging and reconstruction filters*, JMRI 26(2):375--385.\n    2007. doi:`10.1002/jmri.20969 <https://doi.org/10.1002/jmri.20969>`_.\n\n  .. [Ganzetti2016] Ganzetti et al., *Intensity inhomogeneity correction of structural MR images:\n    a data-driven approach to define input algorithm parameters*. Front Neuroinform 10:10. 2016.\n    doi:`10.3389/fninf.2016.00010 <https://doi.org/10.3389/fninf.2016.00010>`_.\n\n  .. [Magnota2006] Magnotta, VA., & Friedman, L., *Measurement of signal-to-noise\n    and contrast-to-noise in the fBIRN multicenter imaging study*.\n    J Dig Imag 19(2):140-147, 2006. doi:`10.1007/s10278-006-0264-x\n    <https://doi.org/10.1007/s10278-006-0264-x>`_.\n\n  .. [Mortamet2009] Mortamet B et al., *Automatic quality assessment in\n    structural brain magnetic resonance imaging*, Mag Res Med 62(2):365-372,\n    2009. doi:`10.1002/mrm.21992 <https://doi.org/10.1002/mrm.21992>`_.\n\n  .. [Tustison2010] Tustison NJ et al., *N4ITK: improved N3 bias correction*,\n    IEEE Trans Med Imag, 29(6):1310-20,\n    2010. doi:`10.1109/TMI.2010.2046908 <https://doi.org/10.1109/TMI.2010.2046908>`_.\n\n  .. [Shehzad2015] Shehzad Z et al., *The Preprocessed Connectomes Project\n     Quality Assessment Protocol - a resource for measuring the quality of MRI data*,\n     Front. Neurosci. Conference Abstract: Neuroinformatics 2015.\n     doi:`10.3389/conf.fnins.2015.91.00047 <https://doi.org/10.3389/conf.fnins.2015.91.00047>`_.\n\n  .. [Forman1995] Forman SD et al., *Improved assessment of significant activation in functional\n     magnetic resonance imaging (fMRI): use of a cluster-size threshold*,\n     Magn. Reson. Med. 33 (5), 636–647, 1995.\n     doi:`10.1002/mrm.1910330508 <https://doi.org/10.1002/mrm.1910330508>`_.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport os.path as op\nfrom math import sqrt\n\nimport numpy as np\nimport scipy.ndimage as nd\nfrom scipy.stats import kurtosis  # pylint: disable=E0611\n\nfrom mriqc import config\n\nDIETRICH_FACTOR = 0.6551364  # 1.0 / sqrt(2 / (4 - pi))\nFSL_FAST_LABELS = {'csf': 1, 'gm': 2, 'wm': 3, 'bg': 0}\n\n\ndef snr(mu_fg, sigma_fg, n):\n    r\"\"\"\n    Calculate the :abbr:`SNR (Signal-to-Noise Ratio)`.\n    The estimation may be provided with only one foreground region in\n    which the noise is computed as follows:\n\n    .. math::\n\n        \\text{SNR} = \\frac{\\mu_F}{\\sigma_F\\sqrt{n/(n-1)}},\n\n    where :math:`\\mu_F` is the mean intensity of the foreground and\n    :math:`\\sigma_F` is the standard deviation of the same region.\n\n    :param float mu_fg: mean of foreground.\n    :param float sigma_fg: standard deviation of foreground.\n    :param int n: number of voxels in foreground mask.\n\n    :return: the computed SNR\n\n    \"\"\"\n    return float(mu_fg / (sigma_fg * sqrt(n / (n - 1))))\n\n\ndef snr_dietrich(mu_fg, mad_air=0.0, sigma_air=1.0):\n    r\"\"\"\n    Calculate the :abbr:`SNR (Signal-to-Noise Ratio)`.\n\n    This must be an air mask around the head, and it should not contain artifacts.\n    The computation is done following the eq. A.12 of [Dietrich2007]_, which\n    includes a correction factor in the estimation of the standard deviation of\n    air and its Rayleigh distribution:\n\n    .. math::\n\n        \\text{SNR} = \\frac{\\mu_F}{\\sqrt{\\frac{2}{4-\\pi}}\\,\\sigma_\\text{air}}.\n\n\n    :param float mu_fg: mean of foreground.\n    :param float sigma_air: standard deviation of the air surrounding the head (\"hat\" mask).\n\n    :return: the computed SNR for the foreground segmentation\n\n    \"\"\"\n    if mad_air > 1.0:\n        return float(DIETRICH_FACTOR * mu_fg / mad_air)\n\n    config.loggers.interface.warning(\n        'Estimated signal variation in the background was too small '\n        f'(MAD={mad_air}, sigma={sigma_air})',\n    )\n    return float(DIETRICH_FACTOR * mu_fg / sigma_air) if sigma_air > 1e-3 else -1.0\n\n\ndef cnr(mu_wm, mu_gm, sigma_air, sigma_wm, sigma_gm):\n    r\"\"\"\n    Calculate the :abbr:`CNR (Contrast-to-Noise Ratio)` [Magnota2006]_.\n    Higher values are better.\n\n    .. math::\n\n        \\text{CNR} = \\frac{|\\mu_\\text{GM} - \\mu_\\text{WM} |}{\\sqrt{\\sigma_B^2 +\n        \\sigma_\\text{WM}^2 + \\sigma_\\text{GM}^2}},\n\n    where :math:`\\sigma_B` is the standard deviation of the noise distribution within\n    the air (background) mask.\n\n\n    :param float mu_wm: mean of signal within white-matter mask.\n    :param float mu_gm: mean of signal within gray-matter mask.\n    :param float sigma_air: standard deviation of the air surrounding the head (\"hat\" mask).\n    :param float sigma_wm: standard deviation within white-matter mask.\n    :param float sigma_gm: standard within gray-matter mask.\n\n    :return: the computed CNR\n\n    \"\"\"\n    return float(abs(mu_wm - mu_gm) / sqrt(sigma_air**2 + sigma_gm**2 + sigma_wm**2))\n\n\ndef cjv(mu_wm, mu_gm, sigma_wm, sigma_gm):\n    r\"\"\"\n    Calculate the :abbr:`CJV (coefficient of joint variation)`, a measure\n    related to :abbr:`SNR (Signal-to-Noise Ratio)` and\n    :abbr:`CNR (Contrast-to-Noise Ratio)` that is presented as a proxy for\n    the :abbr:`INU (intensity non-uniformity)` artifact [Ganzetti2016]_.\n    Lower is better.\n\n    .. math::\n\n        \\text{CJV} = \\frac{\\sigma_\\text{WM} + \\sigma_\\text{GM}}{|\\mu_\\text{WM} - \\mu_\\text{GM}|}.\n\n    :param float mu_wm: mean of signal within white-matter mask.\n    :param float mu_gm: mean of signal within gray-matter mask.\n    :param float sigma_wm: standard deviation of signal within white-matter mask.\n    :param float sigma_gm: standard deviation of signal within gray-matter mask.\n\n    :return: the computed CJV\n\n\n    \"\"\"\n    return float((sigma_wm + sigma_gm) / abs(mu_wm - mu_gm))\n\n\ndef fber(img, headmask, rotmask=None, decimals=4):\n    r\"\"\"\n    Calculate the :abbr:`FBER (Foreground-Background Energy Ratio)` [Shehzad2015]_,\n    defined as the mean energy of image values within the head relative\n    to outside the head.\n    Higher values are better, and an FBER=-1.0 indicates that there is no signal\n    outside the head mask (e.g., a skull-stripped dataset).\n\n    .. math::\n\n        \\text{FBER} = \\frac{E[|F|^2]}{E[|B|^2]}\n\n\n    :param numpy.ndarray img: input data\n    :param numpy.ndarray headmask: a mask of the head (including skull, skin, etc.)\n    :param numpy.ndarray rotmask: a mask of empty voxels inserted after a rotation of\n      data\n\n    \"\"\"\n\n    fg_mu = np.median(np.abs(img[headmask > 0]) ** 2)\n\n    airmask = np.ones_like(headmask, dtype=np.uint8)\n    airmask[headmask > 0] = 0\n    if rotmask is not None:\n        airmask[rotmask > 0] = 0\n    bg_mu = np.median(np.abs(img[airmask == 1]) ** 2)\n    if bg_mu < 1.0e-3:\n        return -1.0\n    return round(float(fg_mu / bg_mu), decimals)\n\n\ndef efc(img, framemask=None, decimals=4):\n    r\"\"\"\n    Calculate the :abbr:`EFC (Entropy Focus Criterion)` [Atkinson1997]_.\n    Uses the Shannon entropy of voxel intensities as an indication of ghosting\n    and blurring induced by head motion. A range of low values is better,\n    with EFC = 0 for all the energy concentrated in one pixel.\n\n    .. math::\n\n        \\text{E} = - \\sum_{j=1}^N \\frac{x_j}{x_\\text{max}}\n        \\ln \\left[\\frac{x_j}{x_\\text{max}}\\right]\n\n    with :math:`x_\\text{max} = \\sqrt{\\sum_{j=1}^N x^2_j}`.\n\n    The original equation is normalized by the maximum entropy, so that the\n    :abbr:`EFC (Entropy Focus Criterion)` can be compared across images with\n    different dimensions:\n\n    .. math::\n\n        \\text{EFC} = \\left( \\frac{N}{\\sqrt{N}} \\, \\log{\\sqrt{N}^{-1}} \\right) \\text{E}\n\n    :param numpy.ndarray img: input data\n    :param numpy.ndarray framemask: a mask of empty voxels inserted after a rotation of\n      data\n\n    \"\"\"\n\n    if framemask is None:\n        framemask = np.zeros_like(img, dtype=np.uint8)\n\n    n_vox = np.sum(1 - framemask)\n    # Calculate the maximum value of the EFC (which occurs any time all\n    # voxels have the same value)\n    efc_max = 1.0 * n_vox * (1.0 / np.sqrt(n_vox)) * np.log(1.0 / np.sqrt(n_vox))\n\n    # Calculate the total image energy\n    b_max = np.sqrt((img[framemask == 0] ** 2).sum())\n\n    # Calculate EFC (add 1e-16 to the image data to keep log happy)\n    return round(\n        float(\n            (1.0 / efc_max)\n            * np.sum(\n                (img[framemask == 0] / b_max) * np.log((img[framemask == 0] + 1e-16) / b_max)\n            ),\n        ),\n        decimals,\n    )\n\n\ndef wm2max(img, mu_wm):\n    r\"\"\"\n    Calculate the :abbr:`WM2MAX (white-matter-to-max ratio)`,\n    defined as the maximum intensity found in the volume w.r.t. the\n    mean value of the white matter tissue. Values close to 1.0 are\n    better:\n\n    .. math ::\n\n        \\text{WM2MAX} = \\frac{\\mu_\\text{WM}}{P_{99.95}(X)}\n\n    \"\"\"\n    return float(mu_wm / np.percentile(img.reshape(-1), 99.95))\n\n\ndef art_qi1(airmask, artmask):\n    r\"\"\"\n    Detect artifacts in the image using the method described in [Mortamet2009]_.\n    Calculates :math:`\\text{QI}_1`, as the proportion of voxels with intensity\n    corrupted by artifacts normalized by the number of voxels in the \"*hat*\"\n    mask (i.e., the background region above the nasio-occipital plane):\n\n    .. math ::\n\n        \\text{QI}_1 = \\frac{1}{N} \\sum\\limits_{x\\in X_\\text{art}} 1\n\n    Near-zero values are better.\n    If :math:`\\text{QI}_1 = -1`, then the \"*hat*\" mask (background) was empty\n    and the dataset is likely a skull-stripped image or has been heavily\n    post-processed.\n\n    :param numpy.ndarray airmask: input air mask, without artifacts\n    :param numpy.ndarray artmask: input artifacts mask\n\n    \"\"\"\n    if airmask.sum() < 1:\n        return -1.0\n\n    # Count the ratio between artifacts and the total voxels in \"hat\" mask\n    return float(artmask.sum() / (airmask.sum() + artmask.sum()))\n\n\ndef art_qi2(\n    img,\n    airmask,\n    min_voxels=int(1e3),\n    max_voxels=int(3e5),\n    save_plot=True,\n    coil_elements=32,\n):\n    r\"\"\"\n    Calculates :math:`\\text{QI}_2`, based on the goodness-of-fit of a centered\n    :math:`\\chi^2` distribution onto the intensity distribution of\n    non-artifactual background (within the \"hat\" mask):\n\n\n    .. math ::\n\n        \\chi^2_n = \\frac{2}{(\\sigma \\sqrt{2})^{2n} \\, (n - 1)!}x^{2n - 1}\\, e^{-\\frac{x}{2}}\n\n    where :math:`n` is the number of coil elements.\n\n    :param numpy.ndarray img: input data\n    :param numpy.ndarray airmask: input air mask without artifacts\n\n    \"\"\"\n\n    from nireports.reportlets.nuisance import plot_qi2\n    from scipy.stats import chi2\n    from sklearn.neighbors import KernelDensity\n\n    # S. Ogawa was born\n    np.random.seed(1191935)\n\n    data = np.nan_to_num(img[airmask > 0], posinf=0.0)\n    data[data < 0] = 0\n\n    # Write out figure of the fitting\n    out_file = op.abspath('error.svg')\n    with open(out_file, 'w') as ofh:\n        ofh.write('<p>Background noise fitting could not be plotted.</p>')\n\n    if (data > 0).sum() < min_voxels:\n        return 0.0, out_file\n\n    data *= 100 / np.percentile(data, 99)\n    modelx = data if len(data) < max_voxels else np.random.choice(data, size=max_voxels)\n\n    x_grid = np.linspace(0.0, 110, 1000)\n\n    # Estimate data pdf with KDE on a random subsample\n    kde_skl = KernelDensity(kernel='gaussian', bandwidth=4.0).fit(modelx[:, np.newaxis])\n    kde = np.exp(kde_skl.score_samples(x_grid[:, np.newaxis]))\n\n    # Find cutoff\n    kdethi = np.argmax(kde[::-1] > kde.max() * 0.5)\n\n    # Fit X^2\n    param = chi2.fit(modelx, coil_elements)\n    chi_pdf = chi2.pdf(x_grid, *param[:-2], loc=param[-2], scale=param[-1])\n\n    # Compute goodness-of-fit (gof)\n    gof = float(np.abs(kde[-kdethi:] - chi_pdf[-kdethi:]).mean())\n    if save_plot:\n        out_file = plot_qi2(x_grid, kde, chi_pdf, modelx, kdethi)\n\n    return gof, out_file\n\n\ndef volume_fraction(pvms):\n    r\"\"\"\n    Computes the :abbr:`ICV (intracranial volume)` fractions\n    corresponding to the (partial volume maps).\n\n    .. math ::\n\n        \\text{ICV}^k = \\frac{\\sum_i p^k_i}{\\sum\\limits_{x \\in X_\\text{brain}} 1}\n\n    :param list pvms: list of :code:`numpy.ndarray` of partial volume maps.\n\n    \"\"\"\n    tissue_vfs = {}\n    total = 0\n    for k, lid in list(FSL_FAST_LABELS.items()):\n        if lid == 0:\n            continue\n        tissue_vfs[k] = pvms[lid - 1].sum()\n        total += tissue_vfs[k]\n\n    for k in list(tissue_vfs.keys()):\n        tissue_vfs[k] /= total\n    return {k: float(v) for k, v in list(tissue_vfs.items())}\n\n\ndef rpve(pvms, seg):\n    \"\"\"\n    Computes the :abbr:`rPVe (residual partial voluming error)`\n    of each tissue class.\n\n    .. math ::\n\n        \\\\text{rPVE}^k = \\\\frac{1}{N} \\\\left[ \\\\sum\\\\limits_{p^k_i \\\n\\\\in [0.5, P_{98}]} p^k_i + \\\\sum\\\\limits_{p^k_i \\\\in [P_{2}, 0.5)} 1 - p^k_i \\\\right]\n\n    \"\"\"\n\n    pvfs = {}\n    for k, lid in list(FSL_FAST_LABELS.items()):\n        if lid == 0:\n            continue\n        pvmap = pvms[lid - 1]\n        pvmap[pvmap < 0.0] = 0.0\n        pvmap[pvmap >= 1.0] = 1.0\n        totalvol = np.sum(pvmap > 0.0)\n        upth = np.percentile(pvmap[pvmap > 0], 98)\n        loth = np.percentile(pvmap[pvmap > 0], 2)\n        pvmap[pvmap < loth] = 0\n        pvmap[pvmap > upth] = 0\n        pvfs[k] = (pvmap[pvmap > 0.5].sum() + (1.0 - pvmap[pvmap <= 0.5]).sum()) / totalvol\n    return {k: float(v) for k, v in list(pvfs.items())}\n\n\ndef summary_stats(\n    data: np.ndarray,\n    pvms: dict[str, np.ndarray],\n    rprec_data: int = 0,\n    rprec_prob: int = 3,\n    decimals: int = 4,\n) -> dict[str, dict[str, float]]:\n    \"\"\"\n    Estimates weighted summary statistics for each tissue distribution in the data.\n\n    This function calculates the mean, median, standard deviation, kurtosis, median\n    absolute deviation (MAD), the 95th and 5th percentiles, and the number of voxels for\n    each tissue distribution defined by a label in the provided partial volume maps (pvms).\n\n    Parameters\n    ----------\n    data : :obj:`~numpy.ndarray` (float, 3D)\n        A three-dimensional array of data from which summary statistics will be extracted.\n    pvms : :obj:`dict` of :obj:`str` keys and :obj:`~numpy.ndarray` (float, 3D) values\n        A dictionary of partial volume maps where the key indicates the label of a\n        region-of-interest (ROI) and the values are three-dimensional arrays matched in size\n        with `data` and containing the probability/fraction of the voxel containing the given\n        label.\n    rprec_data : :obj:`int`, optional (default=0)\n        Number of decimal places to round the data array before calculation. Rounding\n        alleviates floating-point error variability by explicitly rounding before\n        quantification operations.\n    rprec_prob : :obj:`int`, optional (default=3)\n        Number of decimal places to round the probability maps before calculation. Rounding\n        alleviates floating-point error variability by explicitly rounding before\n        quantification operations.\n\n    Returns\n    -------\n    :obj:`dict`\n        A dictionary where the keys are labels from the ``pvms`` dictionary and the values\n        are dictionaries containing the following keys for each tissue distribution:\n\n            * ``'mean'``: :obj:`float` - Mean value\n            * ``'median'``: :obj:`float` - Median value\n            * ``'p95'``: :obj:`float` - 95th percentile\n            * ``'p05'``: :obj:`float` - 5th percentile\n            * ``'k'``: :obj:`float` - Kurtosis\n            * ``'stdv'``: :obj:`float` - Standard deviation\n            * ``'mad'``: :obj:`float` - Median absolute deviation\n            * ``'n'``: :obj:`int` - Number of voxels in the tissue distribution\n\n    \"\"\"\n    from statsmodels.robust.scale import mad\n    from statsmodels.stats.weightstats import DescrStatsW\n\n    output = {}\n    for label, probmap in pvms.items():\n        wstats = DescrStatsW(\n            data=np.round(data.reshape(-1), rprec_data),\n            weights=np.round(probmap.astype(np.float32).reshape(-1), rprec_prob),\n        )\n        nvox = probmap.sum()\n        p05, median, p95 = wstats.quantile(np.array([0.05, 0.50, 0.95]), return_pandas=False)\n        thresholded = data[probmap > (0.5 * probmap.max())]\n\n        output[label] = {\n            'mean': round(float(wstats.mean), decimals),\n            'median': round(float(median), decimals),\n            'p95': round(float(p95), decimals),\n            'p05': round(float(p05), decimals),\n            'k': round(float(kurtosis(thresholded)), decimals),\n            'stdv': round(float(wstats.std), decimals),\n            'mad': round(float(mad(thresholded, center=median)), decimals),\n            'n': float(nvox),\n        }\n\n    return output\n\n\ndef _prepare_mask(mask, label, erode=True):\n    fgmask = mask.copy()\n\n    if np.issubdtype(fgmask.dtype, np.integer):\n        if isinstance(label, (str, bytes)):\n            label = FSL_FAST_LABELS[label]\n\n        fgmask[fgmask != label] = 0\n        fgmask[fgmask == label] = 1\n    else:\n        fgmask[fgmask > 0.95] = 1.0\n        fgmask[fgmask < 1.0] = 0\n\n    if erode:\n        # Create a structural element to be used in an opening operation.\n        struct = nd.generate_binary_structure(3, 2)\n        # Perform an opening operation on the background data.\n        fgmask = nd.binary_opening(fgmask, structure=struct).astype(np.uint8)\n\n    return fgmask\n"
  },
  {
    "path": "mriqc/qc/diffusion.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\n\"\"\"\nImage quality metrics for diffusion MRI data\n============================================\n\nIQMs relating to spatial information\n------------------------------------\nDefinitions are given in the :ref:`summary of structural IQMs <iqms_t1w>`.\n\n.. _iqms_efc:\n\n- **Entropy-focus criterion** (:py:func:`~mriqc.qc.anatomical.efc`).\n\n.. _iqms_fber:\n\n- **Foreground-Background energy ratio** (:py:func:`~mriqc.qc.anatomical.fber`,  [Shehzad2015]_).\n\n.. _iqms_fwhm:\n\n- **Full-width half maximum smoothness** (``fwhm_*``, see [Friedman2008]_).\n\n.. _iqms_snr:\n\n- **Signal-to-noise ratio** (:py:func:`~mriqc.qc.anatomical.snr`).\n\n.. _iqms_summary:\n\n- **Summary statistics** (:py:func:`~mriqc.qc.anatomical.summary_stats`).\n\nIQMs relating to diffusion weighting\n------------------------------------\nIQMs specific to diffusion weighted imaging.\n\n.. _iqms_piesno:\n\nNoise in raw dMRI estimated with PIESNO (``piesno_sigma``)\n    Employs PIESNO (Probabilistic Identification and Estimation\n    of Noise) algorithm [Koay2009]_ to estimate the standard deviation (sigma) of the\n    noise in each voxel of a 4D dMRI data array.\n\n.. _iqms_cc_snr:\n\nSNR estimated in the Corpus Callosum (``cc_snr``)\n    Worst-case and best-case signal-to-noise ratio (SNR) within the corpus callosum.\n\n.. _iqms_fa_nans:\n\nNumber of not-a-number (NaN) values in the FA map (``fa_nan``)\n    Fraction of NaNs within the brain mask, in ppm.\n\n.. _iqms_fa_degenerate:\n\nNumber of degenerate modeled voxels in the FA map (``fa_degenerate``)\n    Fraction of invalid FA values (i.e., outside the [0, 1] closed range) within\n    the brain mask, in ppm.\n\nIQMs relating artifacts and other\n---------------------------------\nIQMs targeting artifacts that are specific of DWI images.\n\n.. _iqms_spike_ppm:\n\nGlobal and slice-wise spike fractions (``spikes_ppm``)\n    Fractions of voxels classified as spikes (in parts-per-million, ppm).\n    The spikes mask is calculated by identifying voxels with signal intensities\n    exceeding a threshold based on standard deviations above the mean.\n\nReferences\n----------\n.. [Koay2009] Koay C.G., E. Ozarslan, C. Pierpaoli. Probabilistic Identification\n       and Estimation of Noise (PIESNO): A self-consistent approach and\n       its applications in MRI. JMR, 199(1):94-103, 2009.\n\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom contextlib import suppress\nfrom warnings import warn\n\nimport numpy as np\nfrom statsmodels.robust.scale import mad\n\nMIN_NUM_CC_MASK = 5\n\n\nclass ExtremeValueWarning(UserWarning):\n    \"\"\"A warning type for dubious metric values.\"\"\"\n\n\ndef noise_b0(\n    in_b0: np.ndarray,\n    percentiles: tuple[float, float, float] = (25.0, 50.0, 75.0),\n    mask: np.ndarray | None = None,\n) -> dict[str, float]:\n    \"\"\"\n    Estimates noise levels in raw dMRI data using the variance of the $b$=0 volumes.\n\n    This function calculates the variance of the $b$=0 volumes in a 4D dMRI array\n    within a provided mask. It then computes the noise estimates at specified\n    percentiles of the variance distribution. This approach assumes that noise primarily\n    contributes to the lower end of the variance distribution.\n\n    Parameters\n    ----------\n    in_b0 : :obj:`~numpy.ndarray`\n        The 3D or 4D dMRI data array. If 4D, the first volume (assumed to be\n        the $b$=0 image) is used for noise estimation.\n    percentiles : :obj:`tuple` (float, float, float), optional (default=(25, 50, 75))\n        A tuple of three integers specifying the percentiles of the variance\n        distribution to use for noise estimation. These percentiles represent\n        different noise levels within the data.\n    mask : :obj:`~numpy.ndarray`, optional (default=``None``)\n        A boolean mask used to restrict the noise estimation to specific brain regions.\n        If ``None``, a mask of ones with the same shape as the first 3 dimensions of\n        ``in_b0`` is used.\n\n    Returns\n    -------\n    :obj:`dict`\n        A dictionary containing the noise estimates at the specified percentiles:\n\n        * keys: :obj:`str` - Percentile values (e.g., '25', '50', '75').\n        * values: :obj:`float` - Noise level estimates at the corresponding percentiles.\n\n    \"\"\"\n\n    if in_b0.ndim != 4:\n        return None\n\n    data = in_b0[np.ones(in_b0.shape[:3], dtype=bool) if mask is None else mask]\n    variance = np.var(data, -1)\n    noise_estimates = dict(\n        zip(\n            (f'{p}' for p in percentiles),\n            np.percentile(variance, percentiles),\n        )\n    )\n\n    return noise_estimates\n\n\ndef cc_snr(\n    in_b0: np.ndarray,\n    dwi_shells: list[np.ndarray],\n    cc_mask: np.ndarray,\n    b_values: np.ndarray,\n    b_vectors: np.ndarray,\n    bval_thres: int = 50,\n    decimals: int = 2,\n) -> dict[int, (float, float)]:\n    \"\"\"\n    Calculates the worst-case and best-case signal-to-noise ratio (SNR) within the corpus callosum.\n\n    This function estimates the SNR in the corpus callosum (CC) by comparing the\n    mean signal intensity within the CC mask to the standard deviation of the background\n    signal (extracted from the b0 image). It performs separate calculations for\n    each diffusion-weighted imaging (DWI) shell.\n\n    **Worst-case SNR:** The mean signal intensity along the diffusion direction with the\n    lowest signal is considered the worst-case scenario.\n\n    **Best-case SNR:** The mean signal intensity averaged across the two diffusion\n    directions with the highest signal is considered the best-case scenario.\n\n    Parameters\n    ----------\n    in_b0 : :obj:`~numpy.ndarray` (float, 3D)\n        T1-weighted or b0 image used for background signal estimation.\n    dwi_shells : list[:obj:`~numpy.ndarray` (float, 4D)]\n        List of DWI data for each diffusion shell.\n    cc_mask : :obj:`~numpy.ndarray` (bool, 3D)\n        Boolean mask of the corpus callosum.\n    b_values : :obj:`~numpy.ndarray` (int)\n        Array of b-values for each DWI volume in ``dwi_shells``.\n    b_vectors : :obj:`~numpy.ndarray` (float)\n        Array of diffusion-encoding vectors for each DWI volume in ``dwi_shells``.\n\n    Returns\n    -------\n    cc_snr_estimates : :obj:`dict`\n        Dictionary containing SNR estimates for each b-value. Keys are the b-values\n        (integers), and values are tuples containing two elements:\n\n        * The first element is the worst-case SNR (float).\n        * The second element is the best-case SNR (float).\n\n        The SNR estimates are zero if there are no sufficient voxels to calculate them\n        (may occur if the number of orientations in the file is very low).\n\n    \"\"\"\n\n    cc_mask = cc_mask > 0  # Ensure it's a boolean mask\n    b_values = np.rint(b_values).astype(np.uint16)\n    n_shells = len(b_values)\n\n    if (nvox_cc := cc_mask.sum()) < MIN_NUM_CC_MASK:\n        warn(f'CC mask is too small ({nvox_cc} voxels)', ExtremeValueWarning, stacklevel=1)\n        cc_snr_estimates = {'shell0': 0}\n        cc_snr_estimates = cc_snr_estimates | {\n            f'shell{shell_index:d}_worst': 0 for shell_index in range(1, n_shells + 1)\n        }\n        cc_snr_estimates = cc_snr_estimates | {\n            f'shell{shell_index:d}_best': 0 for shell_index in range(1, n_shells + 1)\n        }\n        return cc_snr_estimates, 0\n\n    std_signal = mad(in_b0[cc_mask])\n    xyz = np.eye(3)\n\n    cc_snr_estimates = {}\n    cc_snr_estimates['shell0'] = round(float(in_b0[cc_mask].mean() / std_signal), decimals)\n\n    # Shell-wise calculation\n    for shell_index, bvecs, shell_data in zip(range(1, n_shells + 1), b_vectors, dwi_shells):\n        shell_data = shell_data[cc_mask]\n\n        # Find main directions of diffusion\n        axis_X = np.argmin(np.sum((bvecs - xyz[0, :]) ** 2, axis=-1))\n        axis_Y = np.argmin(np.sum((bvecs - xyz[1, :]) ** 2, axis=-1))\n        axis_Z = np.argmin(np.sum((bvecs - xyz[2, :]) ** 2, axis=-1))\n\n        mean_signal_worst = 0\n        with suppress(IndexError):\n            data_X = shell_data[..., axis_X]\n            mean_signal_worst = np.mean(data_X)\n\n        mean_signal_best = 0\n        with suppress(IndexError):\n            data_Y = shell_data[..., axis_Y]\n            data_Z = shell_data[..., axis_Z]\n            mean_signal_best = 0.5 * (np.mean(data_Y) + np.mean(data_Z))\n\n        cc_snr_estimates[f'shell{shell_index:d}_worst'] = round(\n            float(np.mean(mean_signal_worst / std_signal)), decimals\n        )\n        cc_snr_estimates[f'shell{shell_index:d}_best'] = round(\n            float(np.mean(mean_signal_best / std_signal)), decimals\n        )\n\n    return cc_snr_estimates, std_signal\n\n\ndef spike_ppm(\n    spike_mask: np.ndarray,\n    slice_threshold: float = 0.05,\n    decimals: int = 2,\n) -> dict[str, float | np.ndarray]:\n    \"\"\"\n    Calculates fractions (global and slice-wise) of voxels classified as spikes in ppm.\n\n    This function computes two metrics:\n\n    * Global spike parts-per-million [ppm]: Fraction of voxels exceeding the spike\n      threshold across the entire data array.\n    * Slice-wise spiking [ppm]: The fraction of slices along each dimension of\n      the data array where the average fraction of spiking voxels within the slice\n      exceeds a user-defined threshold (``slice_threshold``).\n\n    Parameters\n    ----------\n    spike_mask : :obj:`~numpy.ndarray` (bool, same shape as data)\n        The binary mask indicating spike voxels (True) and non-spike voxels (False).\n    slice_threshold : :obj:`float`, optional (default=0.05)\n        The minimum fraction of voxels in a slice that must be classified as spikes\n        for the slice to be considered spiking.\n    decimals : :obj:`int`\n        The number of decimals to round the fractions.\n\n    Returns\n    -------\n    :obj:`dict`\n        A dictionary containing the calculated spike percentages:\n\n        * 'global': :obj:`float` - global spiking voxels ppm.\n        * 'slice_{i,j,k,t}': :obj:`float` - Slice-wise spiking voxel\n          fractions in ppm for each dimension of the data array.\n\n    \"\"\"\n\n    axisnames = 'ijkt'\n\n    spike_global = round(float(1e6 * np.mean(np.ravel(spike_mask))), decimals)\n    spike_slice = {\n        f'slice_{axisnames[axis]}': round(\n            float(1e6 * np.mean(np.mean(spike_mask, axis=axis) > slice_threshold)), decimals\n        )\n        for axis in range(min(spike_mask.ndim, 3))\n    }\n\n    return {'global': spike_global} | spike_slice\n\n\ndef neighboring_dwi_correlation(\n    dwi_data: np.ndarray,\n    neighbor_indices: list[tuple[int, int]],\n    mask: np.ndarray | None = None,\n    decimals: int = 4,\n) -> float:\n    \"\"\"\n    Calculates the Neighboring DWI Correlation (NDC) from diffusion MRI (dMRI) data.\n\n    The NDC is a measure of the correlation between signal intensities in neighboring\n    diffusion-weighted images (DWIs) within a mask. A low NDC (typically below 0.4)\n    can indicate poor image quality, according to Yeh et al. [Yeh2019]_.\n\n    Parameters\n    ----------\n    dwi_data : 4D :obj:`~numpy.ndarray`\n        DWI data on which to calculate NDC\n    neighbor_indices : :obj:`list` of :obj:`tuple`\n        List of (from, to) index neighbors.\n    mask : 3D :obj:`~numpy.ndarray`, optional\n        optional mask of voxels to include in the NDC calculation\n\n    Returns\n    -------\n    :obj:`float`\n        The NDC value.\n\n    References\n    ----------\n    .. [Yeh2019] Yeh, Fang-Cheng, et al. \"Differential tractography as a\n                 track-based biomarker for neuronal injury.\"\n                 NeuroImage 202 (2019): 116131.\n\n    Notes\n    -----\n    This is a copy of DIPY's code to be removed (and just imported) as soon as\n    a new release of DIPY is cut including\n    `dipy/dipy#3156 <https://github.com/dipy/dipy/pull/3156>`__.\n\n    \"\"\"\n\n    neighbor_correlations = []\n\n    mask = np.ones_like(dwi_data[..., 0], dtype=bool) if mask is None else mask\n\n    dwi_data = dwi_data[mask]\n\n    for from_index, to_index in neighbor_indices:\n        flat_from_image = dwi_data[from_index]\n        flat_to_image = dwi_data[to_index]\n\n        neighbor_correlations.append(np.corrcoef(flat_from_image, flat_to_image)[0, 1])\n\n    return round(float(np.mean(neighbor_correlations)), decimals)\n"
  },
  {
    "path": "mriqc/qc/functional.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\nr\"\"\"\nMeasures for the spatial information\n====================================\n\nDefinitions are given in the\n:ref:`summary of structural IQMs <iqms_t1w>`.\n\n.. _iqms_efc:\n\n- **Entropy-focus criterion** (:py:func:`~mriqc.qc.anatomical.efc`).\n\n.. _iqms_fber:\n\n- **Foreground-Background energy ratio** (:py:func:`~mriqc.qc.anatomical.fber`,  [Shehzad2015]_).\n\n.. _iqms_fwhm:\n\n- **Full-width half maximum smoothness** (``fwhm_*``, see [Friedman2008]_).\n\n.. _iqms_snr:\n\n- **Signal-to-noise ratio** (:py:func:`~mriqc.qc.anatomical.snr`).\n\n.. _iqms_summary:\n\n- **Summary statistics** (:py:func:`~mriqc.qc.anatomical.summary_stats`).\n\n\nMeasures for the temporal information\n-------------------------------------\n\n.. _iqms_dvars :\n\nDVARS\n  D referring to temporal derivative of timecourses, VARS referring to\n  RMS variance over voxels ([Power2012]_ ``dvars_nstd``) indexes the rate of change of\n  BOLD signal across the entire brain at each frame of data. DVARS is calculated\n  `with nipype\n  <https://nipype.readthedocs.io/en/latest/api/generated/nipype.algorithms.confounds.html#computedvars>`_\n  after motion correction:\n\n  .. math ::\n\n      \\text{DVARS}_t = \\sqrt{\\frac{1}{N}\\sum_i \\left[x_{i,t} - x_{i,t-1}\\right]^2}\n\n\n  .. note ::\n\n    Intensities are scaled to 1000 leading to the units being expressed in x10\n    :math:`\\%\\Delta\\text{BOLD}` change.\n\n  .. note ::\n\n    MRIQC calculates two additional standardized values of the DVARS.\n    The ``dvars_std`` metric is normalized with the standard deviation of the\n    temporal difference time series. The ``dvars_vstd`` is a voxel-wise\n    standardization of DVARS, where the temporal difference time series is\n    normalized across time by that voxel standard deviation across time, before\n    computing the RMS of the temporal difference [Nichols2013]_.\n\n.. _iqms_gcor:\n\nGlobal Correlation (``gcor``)\n  calculates an optimized summary of time-series\n  correlation as in [Saad2013]_ using AFNI's ``@compute_gcor``:\n\n  .. math ::\n\n      \\text{GCOR} = \\frac{1}{N}\\mathbf{g}_u^T\\mathbf{g}_u\n\n  where :math:`\\mathbf{g}_u` is the average of all unit-variance time series in a\n  :math:`T` (# timepoints) :math:`\\times` :math:`N` (# voxels) matrix.\n\n.. _iqms_tsnr:\n\nTemporal SNR (:abbr:`tSNR (temporal SNR)`, ``tsnr``)\n  is a simplified interpretation of the tSNR definition [Kruger2001]_.\n  We report the median value\n  of the `tSNR map\n  <https://nipype.readthedocs.io/en/latest/api/generated/nipype.algorithms.confounds.html#tsnr>`_\n  calculated like:\n\n  .. math ::\n\n      \\text{tSNR} = \\frac{\\langle S \\rangle_t}{\\sigma_t},\n\n  where :math:`\\langle S \\rangle_t` is the average BOLD signal (across time),\n  and :math:`\\sigma_t` is the corresponding temporal standard-deviation map. Higher\n  values are better.\n\n\nMeasures for artifacts and other\n--------------------------------\n\n.. _iqms_fd:\n\nFramewise Displacement\n  expresses instantaneous head-motion [Jenkinson2002]_.\n  MRIQC reports the average FD, labeled as ``fd_mean``.\n  Rotational displacements are calculated as the displacement on the surface of a\n  sphere of radius 50 mm [Power2012]_:\n\n  .. math ::\n\n      \\text{FD}_t = |\\Delta d_{x,t}| + |\\Delta d_{y,t}| +\n      |\\Delta d_{z,t}| + |\\Delta \\alpha_t| + |\\Delta \\beta_t| + |\\Delta \\gamma_t|\n\n  Along with the base framewise displacement, MRIQC reports the\n  **number of timepoints above FD threshold** (``fd_num``), and the\n  **percent of FDs above the FD threshold** w.r.t. the full timeseries (``fd_perc``).\n  In both cases, the threshold is set at 0.20mm.\n\n.. _iqms_gsr:\n\nGhost to Signal Ratio (:py:func:`~mriqc.qc.functional.gsr`)\n  labeled in the reports as ``gsr_x`` and ``gsr_y``\n  (calculated along the two possible phase-encoding axes **x**, **y**):\n\n  .. math ::\n\n      \\text{GSR} = \\frac{\\mu_G - \\mu_{NG}}{\\mu_S}\n\n  .. image :: ../_static/epi-gsrmask.png\n    :width: 200px\n    :align: center\n\n.. _iqms_aor:\n\nAFNI's outlier ratio (``aor``)\n  Mean fraction of outliers per fMRI volume\n  as given by AFNI's ``3dToutcount``.\n\n.. _iqms_aqi:\n\nAFNI's quality index (``aqi``)\n  Mean quality index as computed by AFNI's ``3dTqual``; for each volume,\n  it is one minus the Spearman's (rank) correlation of that volume with the\n  median volume. Lower values are better.\n\n.. _iqms_dummy:\n\nNumber of *dummy* scans** (``dummy``)\n  A number of volumes in the beginning of the\n  fMRI timeseries identified as non-steady state.\n\n.. topic:: References\n\n  .. [Atkinson1997] Atkinson et al., *Automatic correction of motion artifacts\n    in magnetic resonance images using an entropy\n    focus criterion*, IEEE Trans Med Imag 16(6):903-910, 1997.\n    doi:`10.1109/42.650886 <https://doi.org/10.1109/42.650886>`_.\n\n  .. [Friedman2008] Friedman, L et al., *Test--retest and between‐site reliability in a multicenter\n    fMRI study*. Hum Brain Mapp, 29(8):958--972, 2008. doi:`10.1002/hbm.20440\n    <https://doi.org/10.1002/hbm.20440>`_.\n\n  .. [Giannelli2010] Giannelli et al., *Characterization of Nyquist ghost in\n    EPI-fMRI acquisition sequences implemented on two clinical 1.5 T MR scanner\n    systems: effect of readout bandwidth and echo spacing*. J App Clin Med Phy,\n    11(4). 2010.\n    doi:`10.1120/jacmp.v11i4.3237 <https://doi.org/10.1120/jacmp.v11i4.3237>`_.\n\n  .. [Jenkinson2002] Jenkinson et al., *Improved Optimisation for the Robust and\n    Accurate Linear Registration and Motion Correction of Brain Images*.\n    NeuroImage, 17(2), 825-841, 2002.\n    doi:`10.1006/nimg.2002.1132 <https://doi.org/10.1006/nimg.2002.1132>`_.\n\n  .. [Kruger2001] Krüger et al., *Physiological noise in oxygenation-sensitive\n    magnetic resonance imaging*, Magn. Reson. Med. 46(4):631-637, 2001.\n    doi:`10.1002/mrm.1240 <https://doi.org/10.1002/mrm.1240>`_.\n\n  .. [Nichols2013] Nichols, `Notes on Creating a Standardized Version of DVARS\n    <https://warwick.ac.uk/fac/sci/statistics/staff/academic-research/nichols/scripts/fsl/standardizeddvars.pdf>`_,\n    2013.\n\n  .. [Power2012] Power et al., *Spurious but systematic correlations in\n    functional connectivity MRI networks arise from subject motion*,\n    NeuroImage 59(3):2142-2154,\n    2012, doi:`10.1016/j.neuroimage.2011.10.018\n    <https://doi.org/10.1016/j.neuroimage.2011.10.018>`_.\n\n  .. [Saad2013] Saad et al. *Correcting Brain-Wide Correlation Differences\n    in Resting-State FMRI*, Brain Conn 3(4):339-352,\n    2013, doi:`10.1089/brain.2013.0156\n    <https://doi.org/10.1089/brain.2013.0156>`_.\n\"\"\"\n\nimport os.path as op\n\nimport numpy as np\n\nRAS_AXIS_ORDER = {'x': 0, 'y': 1, 'z': 2}\n\n\ndef gsr(epi_data, mask, direction='y', ref_file=None, out_file=None):\n    \"\"\"\n    Compute the :abbr:`GSR (ghost to signal ratio)` [Giannelli2010]_.\n\n    The procedure is as follows:\n\n      #. Create a Nyquist ghost mask by circle-shifting the original mask by :math:`N/2`.\n\n      #. Rotate by :math:`N/2`\n\n      #. Remove the intersection with the original mask\n\n      #. Generate a non-ghost background\n\n      #. Calculate the :abbr:`GSR (ghost to signal ratio)`\n\n\n    .. warning ::\n\n      This should be used with EPI images for which the phase\n      encoding direction is known.\n\n    :param str epi_file: path to epi file\n    :param str mask_file: path to brain mask\n    :param str direction: the direction of phase encoding (x, y, all)\n    :return: the computed gsr\n\n    \"\"\"\n    direction = direction.lower()\n    if direction[-1] not in ('x', 'y', 'all'):\n        raise Exception(f'Unknown direction {direction}, should be one of x, -x, y, -y, all')\n\n    if direction == 'all':\n        result = []\n        for newdir in ('x', 'y'):\n            ofile = None\n            if out_file is not None:\n                fname, ext = op.splitext(ofile)\n                if ext == '.gz':\n                    fname, ext2 = op.splitext(fname)\n                    ext = ext2 + ext\n                ofile = f'{fname}_{newdir}{ext}'\n            result += [gsr(epi_data, mask, newdir, ref_file=ref_file, out_file=ofile)]\n        return result\n\n    # Roll data of mask through the appropriate axis\n    axis = RAS_AXIS_ORDER[direction]\n    n2_mask = np.roll(mask, mask.shape[axis] // 2, axis=axis)\n\n    # Step 3: remove from n2_mask pixels inside the brain\n    n2_mask = n2_mask * (1 - mask)\n\n    # Step 4: non-ghost background region is labeled as 2\n    n2_mask = n2_mask + 2 * (1 - n2_mask - mask)\n\n    # Step 5: signal is the entire foreground image\n    ghost = np.mean(epi_data[n2_mask == 1]) - np.mean(epi_data[n2_mask == 2])\n    signal = np.median(epi_data[n2_mask == 0])\n    return float(ghost / signal)\n"
  },
  {
    "path": "mriqc/qc/tests/__init__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n"
  },
  {
    "path": "mriqc/qc/tests/test_anatomical.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nAnatomical tests\n\"\"\"\n\nfrom shutil import rmtree\nfrom tempfile import mkdtemp\n\nimport numpy as np\nimport pytest\nfrom scipy.stats import rice\n\n# from numpy.testing import allclose\nfrom ..anatomical import art_qi2\n\n\nclass GroundTruth:\n    def get_data(self, sigma, noise='normal'):\n        \"\"\"Generates noisy 3d data\"\"\"\n        size = (50, 50, 50)\n        test_data = np.ones(size)\n        wmdata = np.zeros(size)\n        bgdata = np.zeros(size)\n        bgdata[:, :25, :] = 1\n        wmdata[bgdata == 0] = 1\n\n        bg_mean = 0\n        wm_mean = 600\n        test_data[bgdata > 0] = bg_mean\n        test_data[wmdata > 0] = wm_mean\n\n        if noise == 'rice':\n            test_data += rice.rvs(0.77, scale=sigma * wm_mean, size=test_data.shape)\n        elif noise == 'rayleigh':\n            test_data += np.random.rayleigh(scale=sigma * wm_mean, size=test_data.shape)\n        else:\n            test_data += np.random.normal(0.0, scale=sigma * wm_mean, size=test_data.shape)\n\n        return test_data, wmdata, bgdata\n\n\n@pytest.fixture\ndef gtruth():\n    return GroundTruth()\n\n\n# @pytest.mark.parametrize(\"sigma\", [0.01, 0.03, 0.05, 0.08, 0.12, 0.15, 0.20])\n# def test_cjv(sigma, rtol=0.1):\n#     size = (50, 50)\n#     test_data = np.ones(size)\n#     wmdata = np.zeros(size)\n#     gmdata = np.zeros(size)\n#     gmdata[:, :25] = 1\n#     wmdata[gmdata == 0] = 1\n\n#     gm_mean = 200\n#     wm_mean = 600\n#     test_data[gmdata > 0] = gm_mean\n#     test_data[wmdata > 0] = wm_mean\n\n#     test_data[wmdata > .5] += np.random.normal(\n#         0.0, scale=sigma*wm_mean, size=test_data[wmdata > .5].shape)\n#     test_data[gmdata > .5] += np.random.normal(\n#         0.0, scale=sigma*gm_mean, size=test_data[gmdata > .5].shape)\n\n#     exp_cjv = sigma * (wm_mean + gm_mean) / (wm_mean - gm_mean)\n\n#     assert np.isclose(cjv(test_data, wmmask=wmdata, gmmask=gmdata), exp_cjv, rtol=rtol)\n\n\n# @pytest.mark.parametrize(\"sigma\", [0.02, 0.03, 0.05, 0.08, 0.12, 0.15, 0.2, 0.4, 0.5])\n# @pytest.mark.parametrize(\"noise\", ['normal', 'rice'])\n# def test_snr(gtruth, sigma, noise):\n#     data, wmdata, _ = gtruth.get_data(sigma, noise)\n#     assert abs(snr(data, wmdata) - (1/sigma)) < 20\n\n\n# @pytest.mark.parametrize(\"sigma\", [0.02, 0.03, 0.05, 0.08, 0.12, 0.15, 0.2, 0.4, 0.5])\n# @pytest.mark.parametrize(\"noise\", ['rice', 'rayleigh'])\n# def test_snr_dietrich(gtruth, sigma, noise):\n#     data, wmdata, bgdata = gtruth.get_data(sigma, noise)\n#     assert abs(snr_dietrich(data, wmdata, bgdata) - (1/sigma)) < 10\n\n\n@pytest.mark.parametrize('sigma', [0.02, 0.03, 0.05, 0.08, 0.12, 0.15, 0.2, 0.4, 0.5])\ndef test_qi2(gtruth, sigma):\n    tmpdir = mkdtemp()\n    data, _, bgdata = gtruth.get_data(sigma, rice)\n    value, _ = art_qi2(data, bgdata, save_plot=False)\n    rmtree(tmpdir)\n    assert value > 0.0\n    assert value < 0.04\n"
  },
  {
    "path": "mriqc/qc/tests/test_diffusion.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\nimport numpy as np\n\nfrom mriqc.qc.diffusion import spike_ppm\n\n\ndef test_spike_ppm():\n    msk = np.random.randint(0, high=2, size=(76, 76, 64, 124), dtype=bool)\n    val = spike_ppm(msk, 0.5)\n\n    assert np.isclose(val['global'], 0.5e6, rtol=1, atol=1)\n\n    assert np.min([val[f'slice_{ax}'] for ax in 'ijk']) >= 0\n    assert np.max([val[f'slice_{ax}'] for ax in 'ijk']) <= 1e6\n    assert len([val[f'slice_{ax}'] for ax in 'ijk']) == msk.ndim - 1\n"
  },
  {
    "path": "mriqc/reports/__init__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nGenerate individual and group reports.\n\nIn order to ease the screening process of individual images, MRIQC\ngenerates individual reports with mosaic views of a number of cutting planes and\nsupporting information (for example, segmentation contours). The most straightforward\nuse-case is the visualization of those images flagged as low-quality by the classifier.\n\nAfter the extraction of :abbr:`IQMs (image quality metrics)` in all the images of our sample,\na group report is generated. The group report shows a scatter plot for each of the\n:abbr:`IQMs (image quality metrics)`, so it is particularly easy to identify the cases that\nare outliers for each metric. The plots are interactive, such that clicking on any particular\nsample opens the corresponding individual report of that case. Examples of group and individual\nreports for the ABIDE dataset are available online at `mriqc.org <https://mriqc.readthedocs.io/>`_.\n\n.. toctree::\n    :maxdepth: 3\n\n    reports/group\n    reports/smri\n    reports/bold\n\nmriqc.reports package\n=====================\nSubmodules\n----------\n.. automodule:: mriqc.reports.group\n    :members:\n    :undoc-members:\n    :show-inheritance:\n\n\"\"\"\n"
  },
  {
    "path": "mriqc/reports/group.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Encapsulates report generation functions.\"\"\"\n\nfrom itertools import product\n\nimport pandas as pd\n\nfrom .. import config\nfrom ..utils.misc import BIDS_COMP\n\n\ndef gen_html(csv_file, mod, csv_failed=None, out_file=None):\n    import os.path as op\n    from datetime import datetime, timezone\n    from io import StringIO as TextIO\n\n    from niworkflows.data import Loader\n\n    from mriqc.data.config import GroupTemplate\n\n    from .. import __version__ as ver\n\n    UTC = timezone.utc\n    load_data = Loader('mriqc')\n\n    if csv_file.suffix == '.csv':\n        dataframe = pd.read_csv(csv_file, index_col=False, dtype=dict.fromkeys(BIDS_COMP, object))\n\n        id_labels = list(set(BIDS_COMP) & set(dataframe.columns))\n        dataframe['label'] = dataframe[id_labels].apply(_format_labels, args=(id_labels,), axis=1)\n    else:\n        dataframe = pd.read_csv(csv_file, index_col=False, sep='\\t', dtype={'bids_name': object})\n        dataframe = dataframe.rename(index=str, columns={'bids_name': 'label'})\n\n    shells_id = list(range(1, 1 + dataframe.columns.str.startswith('efc_shell').sum()))\n\n    QCGROUPS = {\n        'T1w': [\n            (['cjv'], None),\n            (['cnr'], None),\n            (['efc'], None),\n            (['fber'], None),\n            (['wm2max'], None),\n            (['snr_csf', 'snr_gm', 'snr_wm'], None),\n            (['snrd_csf', 'snrd_gm', 'snrd_wm'], None),\n            (['fwhm_avg', 'fwhm_x', 'fwhm_y', 'fwhm_z'], 'vox'),\n            (['qi_1', 'qi_2'], None),\n            (['inu_range', 'inu_med'], None),\n            (['icvs_csf', 'icvs_gm', 'icvs_wm'], None),\n            (['rpve_csf', 'rpve_gm', 'rpve_wm'], None),\n            (['tpm_overlap_csf', 'tpm_overlap_gm', 'tpm_overlap_wm'], None),\n            (\n                [\n                    'summary_bg_mean',\n                    'summary_bg_median',\n                    'summary_bg_stdv',\n                    'summary_bg_mad',\n                    'summary_bg_k',\n                    'summary_bg_p05',\n                    'summary_bg_p95',\n                ],\n                None,\n            ),\n            (\n                [\n                    'summary_csf_mean',\n                    'summary_csf_median',\n                    'summary_csf_stdv',\n                    'summary_csf_mad',\n                    'summary_csf_k',\n                    'summary_csf_p05',\n                    'summary_csf_p95',\n                ],\n                None,\n            ),\n            (\n                [\n                    'summary_gm_mean',\n                    'summary_gm_median',\n                    'summary_gm_stdv',\n                    'summary_gm_mad',\n                    'summary_gm_k',\n                    'summary_gm_p05',\n                    'summary_gm_p95',\n                ],\n                None,\n            ),\n            (\n                [\n                    'summary_wm_mean',\n                    'summary_wm_median',\n                    'summary_wm_stdv',\n                    'summary_wm_mad',\n                    'summary_wm_k',\n                    'summary_wm_p05',\n                    'summary_wm_p95',\n                ],\n                None,\n            ),\n        ],\n        'T2w': [\n            (['cjv'], None),\n            (['cnr'], None),\n            (['efc'], None),\n            (['fber'], None),\n            (['wm2max'], None),\n            (['snr_csf', 'snr_gm', 'snr_wm'], None),\n            (['snrd_csf', 'snrd_gm', 'snrd_wm'], None),\n            (['fwhm_avg', 'fwhm_x', 'fwhm_y', 'fwhm_z'], 'mm'),\n            (['qi_1', 'qi_2'], None),\n            (['inu_range', 'inu_med'], None),\n            (['icvs_csf', 'icvs_gm', 'icvs_wm'], None),\n            (['rpve_csf', 'rpve_gm', 'rpve_wm'], None),\n            (['tpm_overlap_csf', 'tpm_overlap_gm', 'tpm_overlap_wm'], None),\n            (\n                [\n                    'summary_bg_mean',\n                    'summary_bg_stdv',\n                    'summary_bg_k',\n                    'summary_bg_p05',\n                    'summary_bg_p95',\n                ],\n                None,\n            ),\n            (\n                [\n                    'summary_csf_mean',\n                    'summary_csf_stdv',\n                    'summary_csf_k',\n                    'summary_csf_p05',\n                    'summary_csf_p95',\n                ],\n                None,\n            ),\n            (\n                [\n                    'summary_gm_mean',\n                    'summary_gm_stdv',\n                    'summary_gm_k',\n                    'summary_gm_p05',\n                    'summary_gm_p95',\n                ],\n                None,\n            ),\n            (\n                [\n                    'summary_wm_mean',\n                    'summary_wm_stdv',\n                    'summary_wm_k',\n                    'summary_wm_p05',\n                    'summary_wm_p95',\n                ],\n                None,\n            ),\n        ],\n        'bold': [\n            (['efc'], None),\n            (['fber'], None),\n            (['fwhm', 'fwhm_x', 'fwhm_y', 'fwhm_z'], 'mm'),\n            ([f'gsr_{a}' for a in ('x', 'y')], None),\n            (['snr'], None),\n            (['dvars_std', 'dvars_vstd'], None),\n            (['dvars_nstd'], None),\n            (['fd_mean'], 'mm'),\n            (['fd_num'], '# timepoints'),\n            (['fd_perc'], '% timepoints'),\n            (['spikes_num'], '# slices'),\n            (['dummy_trs'], '# TRs'),\n            (['gcor'], None),\n            (['tsnr'], None),\n            (['aor'], None),\n            (['aqi'], None),\n            (\n                [\n                    'summary_bg_mean',\n                    'summary_bg_stdv',\n                    'summary_bg_k',\n                    'summary_bg_p05',\n                    'summary_bg_p95',\n                ],\n                None,\n            ),\n            (\n                [\n                    'summary_fg_mean',\n                    'summary_fg_stdv',\n                    'summary_fg_k',\n                    'summary_fg_p05',\n                    'summary_fg_p95',\n                ],\n                None,\n            ),\n        ],\n        'dwi': [\n            ([f'bdiffs_{sub}' for sub in ('max', 'mean', 'median', 'min')], 'rad'),\n            ([f'efc_shell{i:02d}' for i in shells_id], None),\n            (['fa_degenerate', 'fa_nans'], 'ppm'),\n            ([f'fber_shell{i:02d}' for i in shells_id], None),\n            (['fd_mean'], 'mm'),\n            (['fd_num'], '# timepoints'),\n            (['fd_perc'], '% timepoints'),\n            (['ndc'], None),\n            (['sigma_cc'], None),\n            (['sigma_pca', 'sigma_piesno'], None),\n            (\n                ['snr_cc_shell0']\n                + [f'snr_cc_shell{s}_{t}' for s, t in product(shells_id, ('best', 'worst'))],\n                None,\n            ),\n            (['spikes_global'] + [f'spikes_slice_{ax}' for ax in 'ijk'], 'ppm'),\n            (\n                [\n                    'summary_bg_p05',\n                    'summary_fg_p05',\n                    'summary_wm_p05',\n                    'summary_bg_mean',\n                    'summary_fg_mean',\n                    'summary_wm_mean',\n                    'summary_bg_median',\n                    'summary_fg_median',\n                    'summary_wm_median',\n                ],\n                'a.u.',\n            ),\n            (\n                [\n                    'summary_bg_p95',\n                    'summary_fg_p95',\n                    'summary_wm_p95',\n                ],\n                'a.u.',\n            ),\n            (\n                [\n                    'summary_bg_stdv',\n                    'summary_fg_stdv',\n                    'summary_wm_stdv',\n                    'summary_bg_mad',\n                    'summary_fg_mad',\n                    'summary_wm_mad',\n                ],\n                None,\n            ),\n            (\n                [\n                    'summary_bg_k',\n                ],\n                None,\n            ),\n            (\n                [\n                    'summary_fg_k',\n                    'summary_wm_k',\n                ],\n                None,\n            ),\n        ],\n    }\n\n    nPart = len(dataframe)\n\n    failed = None\n    if csv_failed is not None and op.isfile(csv_failed):\n        config.loggers.cli.warning(f'Found failed-workflows table \"{csv_failed}\"')\n        failed_df = pd.read_csv(csv_failed, index_col=False)\n        cols = list(set(id_labels) & set(failed_df.columns))\n\n        try:\n            failed_df = failed_df.sort_values(by=cols)\n        except AttributeError:\n            failed_df = failed_df.sort(columns=cols)\n\n        # myfmt not defined\n        # failed = failed_df[cols].apply(myfmt, args=(cols,), axis=1).ravel().tolist()\n\n    csv_groups = []\n    datacols = dataframe.columns.tolist()\n    for group, units in QCGROUPS[mod]:\n        dfdict = {'iqm': [], 'value': [], 'label': [], 'units': []}\n\n        for iqm in group:\n            if iqm in datacols:\n                values = dataframe[[iqm]].values.ravel().tolist()\n                if values:\n                    dfdict['iqm'] += [iqm] * nPart\n                    dfdict['units'] += [units] * nPart\n                    dfdict['value'] += values\n                    dfdict['label'] += dataframe[['label']].values.ravel().tolist()\n\n        # Save only if there are values\n        if dfdict['value']:\n            csv_df = pd.DataFrame(dfdict)\n            csv_str = TextIO()\n            csv_df[['iqm', 'value', 'label', 'units']].to_csv(csv_str, index=False)\n            csv_groups.append(csv_str.getvalue())\n\n    if out_file is None:\n        out_file = op.abspath('group.html')\n    tpl = GroupTemplate()\n    tpl.generate_conf(\n        {\n            'modality': mod,\n            'timestamp': datetime.now(tz=UTC).strftime('%Y-%m-%d, %H:%M'),\n            'version': ver,\n            'csv_groups': csv_groups,\n            'failed': failed,\n            'boxplots_js': load_data('data/reports/embed_resources/boxplots.js').read_text(),\n            'd3_js': load_data('data/reports/embed_resources/d3.min.js').read_text(),\n            'boxplots_css': load_data('data/reports/embed_resources/boxplots.css').read_text(),\n        },\n        out_file,\n    )\n\n    return out_file\n\n\ndef _format_labels(row, id_labels):\n    \"\"\"format participant labels\"\"\"\n    crow = (\n        f'{prefix}-{row[[col_id]].values[0]}'\n        for col_id, prefix in list(BIDS_COMP.items())\n        if col_id in id_labels\n    )\n    return '_'.join(crow)\n"
  },
  {
    "path": "mriqc/reports/individual.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Encapsulates report generation functions.\"\"\"\n\nfrom json import loads\nfrom pathlib import Path\n\nfrom nireports.assembler.report import Report\nfrom niworkflows.data import Loader\n\n_load_data = Loader('mriqc')\n\n\ndef generate_reports():\n    \"\"\"Generate the reports associated with an MRIQC run.\"\"\"\n\n    from mriqc import config\n\n    config.loggers.workflow.info('Generating reports...')\n    output_files = [_single_report(ff) for mod in config.workflow.inputs.values() for ff in mod]\n    config.loggers.workflow.info(f'Report generation finished ({len(output_files)} reports).')\n    return output_files\n\n\ndef _single_report(in_file):\n    \"\"\"Generate a single report.\"\"\"\n    from mriqc import config\n\n    # Ensure it's a Path\n    in_file = Path(in_file if not isinstance(in_file, list) else in_file[0])\n\n    # Extract BIDS entities\n    entities = config.execution.layout.get_file(in_file).get_entities()\n    entities.pop('extension', None)\n    entities.pop('echo', None)\n    entities.pop('part', None)\n    report_type = entities.pop('datatype', None)\n\n    # Read output file:\n    mriqc_json = loads(\n        (\n            Path(config.execution.output_dir)\n            / in_file.parent.relative_to(config.execution.bids_dir)\n            / in_file.name.replace(''.join(in_file.suffixes), '.json')\n        ).read_text()\n    )\n    mriqc_json.pop('bids_meta')\n\n    # Clean-up provenance dictionary\n    prov = mriqc_json.pop('provenance', None)\n    prov.pop('webapi_url', None)\n    prov.pop('webapi_port', None)\n    prov.pop('settings', None)\n    prov.pop('software', None)\n    prov.update({f'warnings_{kk}': vv for kk, vv in prov.pop('warnings', {}).items()})\n    prov['Input filename'] = f'<BIDS root>/{in_file.relative_to(config.execution.bids_dir)}'\n    prov['Versions_MRIQC'] = prov.pop('version', config.environment.version)\n    prov['Execution environment'] = config.environment.exec_env\n    prov['Versions_NiPype'] = config.environment.nipype_version\n    prov['Versions_TemplateFlow'] = config.environment.templateflow_version\n\n    bids_meta = config.execution.layout.get_file(in_file).get_metadata()\n    bids_meta.pop('global', None)\n\n    robj = Report(\n        config.execution.output_dir,\n        config.execution.run_uuid,\n        reportlets_dir=config.execution.work_dir / 'reportlets',\n        bootstrap_file=_load_data(f'data/bootstrap-{report_type}.yml'),\n        metadata={\n            'dataset': config.execution.dsname,\n            'about-metadata': {\n                'Provenance Information': prov,\n                'Dataset Information': bids_meta,\n                'Extracted Image quality metrics (IQMs)': mriqc_json,\n            },\n        },\n        plugin_meta={\n            'rating-widget': {\n                'filename': in_file.name,\n                'dataset': config.execution.dsname,\n                'access_token': config.execution.webapi_token,\n                'endpoint': f'{config.execution.webapi_url}/rating',\n            },\n        },\n        **entities,\n    )\n    robj.generate_report()\n    return robj.out_filename.absolute()\n"
  },
  {
    "path": "mriqc/synthstrip/ORIGINAL_LICENSE",
    "content": "         FreeSurfer Software License Agreement (\"Agreement\")\n                    Version 1.0 (February 2011)\n\nThis Agreement covers contributions to and downloads from the\nFreeSurfer project (\"FreeSurfer\") maintained by The General Hospital\nCorporation, Boston MA, USA (\"MGH\"). Part A of this Agreement applies to\ncontributions of software and/or data to FreeSurfer (including making\nrevisions of or additions to code and/or data already in FreeSurfer). Part\nB of this Agreement applies to downloads of software and/or data from\nFreeSurfer. Part C of this Agreement applies to all transactions with\nFreeSurfer. If you distribute Software (as defined below) downloaded from\nFreeSurfer, all of the paragraphs of Part B of this Agreement must be\nincluded with and apply to such Software.\n\nYour contribution of software and/or data to FreeSurfer (including prior\nto the date of the first publication of this Agreement, each a\n\"Contribution\") and/or downloading, copying, modifying, displaying,\ndistributing or use of any software and/or data from FreeSurfer\n(collectively, the \"Software\") constitutes acceptance of all of the\nterms and conditions of this Agreement. If you do not agree to such\nterms and conditions, you have no right to contribute your\nContribution, or to download, copy, modify, display, distribute or use\nthe Software.\n\nPART A. CONTRIBUTION AGREEMENT - License to MGH with Right to Sublicense\n(\"Contribution Agreement\").\n\n1. As used in this Contribution Agreement, \"you\" means the individual\n   contributing the Contribution to FreeSurfer and the institution or\n   entity which employs or is otherwise affiliated with such\n   individual in connection with such Contribution.\n\n2. This Contribution Agreement applies to all Contributions made to\n   FreeSurfer, including without limitation Contributions made prior to\n   the date of first publication of this Agreement. If at any time you\n   make a Contribution to FreeSurfer, you represent that (i) you are\n   legally authorized and entitled to make such Contribution and to\n   grant all licenses granted in this Contribution Agreement with\n   respect to such Contribution; (ii) if your Contribution includes\n   any patient data, all such data is de-identified in accordance with\n   U.S. confidentiality and security laws and requirements, including\n   but not limited to the Health Insurance Portability and\n   Accountability Act (HIPAA) and its regulations, and your disclosure\n   of such data for the purposes contemplated by this Agreement is\n   properly authorized and in compliance with all applicable laws and\n   regulations; and (iii) you have preserved in the Contribution all\n   applicable attributions, copyright notices and licenses for any\n   third party software or data included in the Contribution.\n\n3. Except for the licenses granted in this Agreement, you reserve all\n   right, title and interest in your Contribution.\n\n4. You hereby grant to MGH, with the right to sublicense, a\n   perpetual, worldwide, non-exclusive, no charge, royalty-free,\n   irrevocable license to use, reproduce, make derivative works of,\n   display and distribute the Contribution. If your Contribution is\n   protected by patent, you hereby grant to MGH, with the right to\n   sublicense, a perpetual, worldwide, non-exclusive, no-charge,\n   royalty-free, irrevocable license under your interest in patent\n   rights covering the Contribution, to make, have made, use, sell and\n   otherwise transfer your Contribution, alone or in combination with\n   any other code.\n\n5. You acknowledge and agree that MGH may incorporate your\n   Contribution into FreeSurfer and may make FreeSurfer available to members\n   of the public on an open source basis under terms substantially in\n   accordance with the Software License set forth in Part B of this\n   Agreement. You further acknowledge and agree that MGH shall\n   have no liability arising in connection with claims resulting from\n   your breach of any of the terms of this Agreement.\n\n6. YOU WARRANT THAT TO THE BEST OF YOUR KNOWLEDGE YOUR CONTRIBUTION\n   DOES NOT CONTAIN ANY CODE THAT REQURES OR PRESCRIBES AN \"OPEN\n   SOURCE LICENSE\" FOR DERIVATIVE WORKS (by way of non-limiting\n   example, the GNU General Public License or other so-called\n   \"reciprocal\" license that requires any derived work to be licensed\n   under the GNU General Public License or other \"open source\n   license\").\n\nPART B. DOWNLOADING AGREEMENT - License from MGH with Right to Sublicense\n(\"Software License\").\n\n1. As used in this Software License, \"you\" means the individual\n   downloading and/or using, reproducing, modifying, displaying and/or\n   distributing the Software and the institution or entity which\n   employs or is otherwise affiliated with such individual in\n   connection therewith. The General Hospital Corporation (\"MGH\")\n   hereby grants you, with right to sublicense, with\n   respect to MGH's rights in the software, and data, if any,\n   which is the subject of this Software License (collectively, the\n   \"Software\"), a royalty-free, non-exclusive license to use,\n   reproduce, make derivative works of, display and distribute the\n   Software, provided that:\n\n(a) you accept and adhere to all of the terms and conditions of this\nSoftware License;\n\n(b) in connection with any copy of or sublicense of all or any portion\nof the Software, all of the terms and conditions in this Software\nLicense shall appear in and shall apply to such copy and such\nsublicense, including without limitation all source and executable\nforms and on any user documentation, prefaced with the following\nwords: \"All or portions of this licensed product (such portions are\nthe \"Software\") have been obtained under license from The General Hospital\nCorporation \"MGH\" and are subject to the following terms and\nconditions:\"\n\n(c) you preserve and maintain all applicable attributions, copyright\nnotices and licenses included in or applicable to the Software;\n\n(d) modified versions of the Software must be clearly identified and\nmarked as such, and must not be misrepresented as being the original\nSoftware; and\n\n(e) you consider making, but are under no obligation to make, the\nsource code of any of your modifications to the Software freely\navailable to others on an open source basis.\n\n2. The license granted in this Software License includes without\n   limitation the right to (i) incorporate the Software into\n   proprietary programs (subject to any restrictions applicable to\n   such programs), (ii) add your own copyright statement to your\n   modifications of the Software, and (iii) provide additional or\n   different license terms and conditions in your sublicenses of\n   modifications of the Software; provided that in each case your use,\n   reproduction or distribution of such modifications otherwise\n   complies with the conditions stated in this Software License.\n\n3. This Software License does not grant any rights with respect to\n   third party software, except those rights that MGH has been\n   authorized by a third party to grant to you, and accordingly you\n   are solely responsible for (i) obtaining any permissions from third\n   parties that you need to use, reproduce, make derivative works of,\n   display and distribute the Software, and (ii) informing your\n   sublicensees, including without limitation your end-users, of their\n   obligations to secure any such required permissions.\n\n4. The Software has been designed for research purposes only and has\n   not been reviewed or approved by the Food and Drug Administration\n   or by any other agency. YOU ACKNOWLEDGE AND AGREE THAT CLINICAL\n   APPLICATIONS ARE NEITHER RECOMMENDED NOR ADVISED. Any\n   commercialization of the Software is at the sole risk of the party\n   or parties engaged in such commercialization. You further agree to\n   use, reproduce, make derivative works of, display and distribute\n   the Software in compliance with all applicable governmental laws,\n   regulations and orders, including without limitation those relating\n   to export and import control.\n\n5. The Software is provided \"AS IS\" and neither MGH nor any\n   contributor to the software (each a \"Contributor\") shall have any\n   obligation to provide maintenance, support, updates, enhancements\n   or modifications thereto. MGH AND ALL CONTRIBUTORS SPECIFICALLY\n   DISCLAIM ALL EXPRESS AND IMPLIED WARRANTIES OF ANY KIND INCLUDING,\n   BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR\n   A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n   MGH OR ANY CONTRIBUTOR BE LIABLE TO ANY PARTY FOR DIRECT,\n   INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES\n   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ARISING IN ANY WAY\n   RELATED TO THE SOFTWARE, EVEN IF MGH OR ANY CONTRIBUTOR HAS\n   BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. TO THE MAXIMUM\n   EXTENT NOT PROHIBITED BY LAW OR REGULATION, YOU FURTHER ASSUME ALL\n   LIABILITY FOR YOUR USE, REPRODUCTION, MAKING OF DERIVATIVE WORKS,\n   DISPLAY, LICENSE OR DISTRIBUTION OF THE SOFTWARE AND AGREE TO\n   INDEMNIFY AND HOLD HARMLESS MGH AND ALL CONTRIBUTORS FROM AND\n   AGAINST ANY AND ALL CLAIMS, SUITS, ACTIONS, DEMANDS AND JUDGMENTS\n   ARISING THEREFROM.\n\n6. None of the names, logos or trademarks of MGH or any of\n   MGH's affiliates or any of the Contributors, or any funding\n   agency, may be used to endorse or promote products produced in\n   whole or in part by operation of the Software or derived from or\n   based on the Software without specific prior written permission\n   from the applicable party.\n\n7. Any use, reproduction or distribution of the Software which is not\n   in accordance with this Software License shall automatically revoke\n   all rights granted to you under this Software License and render\n   Paragraphs 1 and 2 of this Software License null and void.\n\n8. This Software License does not grant any rights in or to any\n   intellectual property owned by MGH or any Contributor except\n   those rights expressly granted hereunder.\n\nPART C. MISCELLANEOUS\n\nThis Agreement shall be governed by and construed in accordance with\nthe laws of The Commonwealth of Massachusetts without regard to\nprinciples of conflicts of law. This Agreement shall supercede and\nreplace any license terms that you may have agreed to previously with\nrespect to FreeSurfer.\n\n"
  },
  {
    "path": "mriqc/synthstrip/__init__.py",
    "content": ""
  },
  {
    "path": "mriqc/synthstrip/__main__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2022 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\nif __name__ == '__main__':\n    import sys\n\n    from mriqc.synthstrip.cli import main\n\n    from . import __name__ as module\n\n    # `python -m <module>` typically displays the command as __main__.py\n    if '__main__.py' in sys.argv[0]:\n        sys.argv[0] = f'{sys.executable} -m {module}'\n    main()\n"
  },
  {
    "path": "mriqc/synthstrip/cli.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2022 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n# STATEMENT OF CHANGES: This file is derived from sources licensed under the FreeSurfer 1.0 license\n# terms, and this file has been changed.\n# The full licensing terms of the original work are found at:\n# https://github.com/freesurfer/freesurfer/blob/2995ded957961a7f3704de57eee88eb6cc30d52d/LICENSE.txt\n# A copy of the license has been archived in the ORIGINAL_LICENSE file\n# found within this redistribution.\n#\n# The original file this work derives from is found at:\n# https://github.com/freesurfer/freesurfer/blob/2995ded957961a7f3704de57eee88eb6cc30d52d/mri_synthstrip/mri_synthstrip\n#\n# [April 2022] CHANGES:\n#    * MAINT: Split the monolithic file into model and CLI submodules\n#    * ENH: Replace freesurfer Python bundle with in-house code.\n#\n\"\"\"\nRobust, universal skull-stripping for brain images of any type.\nIf you use SynthStrip in your analysis, please cite:\n\n  A Hoopes, JS Mora, AV Dalca, B Fischl, M Hoffmann.\n  SynthStrip: Skull-Stripping for Any Brain Image.\n  https://arxiv.org/abs/2203.09974\n\n\"\"\"\n\n\ndef main():\n    \"\"\"Entry point to SynthStrip.\"\"\"\n    import os\n    from argparse import ArgumentParser\n\n    import nibabel as nb\n    import numpy as np\n    import scipy\n    import torch\n\n    from .model import StripModel\n\n    # parse command line\n    parser = ArgumentParser(description=__doc__)\n    parser.add_argument(\n        '-i',\n        '--image',\n        metavar='file',\n        required=True,\n        help='Input image to skullstrip.',\n    )\n    parser.add_argument('-o', '--out', metavar='file', help='Save stripped image to path.')\n    parser.add_argument('-m', '--mask', metavar='file', help='Save binary brain mask to path.')\n    parser.add_argument('-g', '--gpu', action='store_true', help='Use the GPU.')\n    parser.add_argument('-n', '--num-threads', action='store', type=int, help='number of threads')\n    parser.add_argument(\n        '-b',\n        '--border',\n        default=1,\n        type=int,\n        help='Mask border threshold in mm. Default is 1.',\n    )\n    parser.add_argument('--model', metavar='file', help='Alternative model weights.')\n    args = parser.parse_args()\n\n    # sanity check on the inputs\n    if not args.out and not args.mask:\n        parser.fatal('Must provide at least --out or --mask output flags.')\n\n    # necessary for speed gains (I think)\n    torch.backends.cudnn.benchmark = True\n    torch.backends.cudnn.deterministic = True\n\n    # configure GPU device\n    if args.gpu:\n        os.environ['CUDA_VISIBLE_DEVICES'] = '0'\n        device = torch.device('cuda')\n        device_name = 'GPU'\n    else:\n        os.environ['CUDA_VISIBLE_DEVICES'] = '-1'\n        device = torch.device('cpu')\n        device_name = 'CPU'\n\n        if args.num_threads and args.num_threads > 0:\n            torch.set_num_threads(args.num_threads)\n\n    # configure model\n    print(f'Configuring model on the {device_name}')\n\n    with torch.no_grad():\n        model = StripModel()\n        model.to(device)\n        model.eval()\n\n    # load model weights\n    if args.model is not None:\n        modelfile = args.model\n        print('Using custom model weights')\n    else:\n        raise RuntimeError('A model must be provided.')\n\n    checkpoint = torch.load(modelfile, map_location=device)\n    model.load_state_dict(checkpoint['model_state_dict'])\n\n    # load input volume\n    print(f'Input image read from: {args.image}')\n\n    # normalize intensities\n    image = nb.load(args.image)\n    conformed = conform(image)\n    in_data = conformed.get_fdata(dtype='float32')\n    in_data -= in_data.min()\n    in_data = np.clip(in_data / np.percentile(in_data, 99), 0, 1)\n    in_data = in_data[np.newaxis, np.newaxis]\n\n    # predict the surface distance transform\n    input_tensor = torch.from_numpy(in_data).to(device)\n    with torch.no_grad():\n        sdt = model(input_tensor).cpu().numpy().squeeze()\n\n    # unconform the sdt and extract mask\n    sdt_target = resample_like(\n        nb.Nifti1Image(sdt, conformed.affine, None),\n        image,\n        output_dtype='int16',\n        cval=100,\n    )\n    sdt_data = np.asanyarray(sdt_target.dataobj).astype('int16')\n\n    # find largest CC (just do this to be safe for now)\n    components = scipy.ndimage.label(sdt_data.squeeze() < args.border)[0]\n    bincount = np.bincount(components.flatten())[1:]\n    mask = components == (np.argmax(bincount) + 1)\n    mask = scipy.ndimage.morphology.binary_fill_holes(mask)\n\n    # write the masked output\n    if args.out:\n        img_data = image.get_fdata()\n        bg = np.min([0, img_data.min()])\n        img_data[mask == 0] = bg\n        nb.Nifti1Image(img_data, image.affine, image.header).to_filename(\n            args.out,\n        )\n        print(f'Masked image saved to: {args.out}')\n\n    # write the brain mask\n    if args.mask:\n        hdr = image.header.copy()\n        hdr.set_data_dtype('uint8')\n        nb.Nifti1Image(mask, image.affine, hdr).to_filename(args.mask)\n        print(f'Binary brain mask saved to: {args.mask}')\n\n    print('If you use SynthStrip in your analysis, please cite:')\n    print('----------------------------------------------------')\n    print('SynthStrip: Skull-Stripping for Any Brain Image.')\n    print('A Hoopes, JS Mora, AV Dalca, B Fischl, M Hoffmann.')\n\n\ndef conform(input_nii):\n    \"\"\"Resample image as SynthStrip likes it.\"\"\"\n    import nibabel as nb\n    import numpy as np\n    from nitransforms.linear import Affine\n\n    shape = np.array(input_nii.shape[:3])\n    affine = input_nii.affine\n\n    # Get corner voxel centers in index coords\n    corner_centers_ijk = (\n        np.array(\n            [\n                (i, j, k)\n                for k in (0, shape[2] - 1)\n                for j in (0, shape[1] - 1)\n                for i in (0, shape[0] - 1)\n            ]\n        )\n        + 0.5\n    )\n\n    # Get corner voxel centers in mm\n    corners_xyz = affine @ np.hstack((corner_centers_ijk, np.ones((len(corner_centers_ijk), 1)))).T\n\n    # Target affine is 1mm voxels in LIA orientation\n    target_affine = np.diag([-1.0, 1.0, -1.0, 1.0])[:, (0, 2, 1, 3)]\n\n    # Target shape\n    extent = corners_xyz.min(1)[:3], corners_xyz.max(1)[:3]\n    target_shape = ((extent[1] - extent[0]) / 1.0 + 0.999).astype(int)\n\n    # SynthStrip likes dimensions be multiple of 64 (192, 256, or 320)\n    target_shape = np.clip(np.ceil(np.array(target_shape) / 64).astype(int) * 64, 192, 320)\n\n    # Ensure shape ordering is LIA too\n    target_shape[2], target_shape[1] = target_shape[1:3]\n\n    # Coordinates of center voxel do not change\n    input_c = affine @ np.hstack((0.5 * (shape - 1), 1.0))\n    target_c = target_affine @ np.hstack((0.5 * (target_shape - 1), 1.0))\n\n    # Rebase the origin of the new, plumb affine\n    target_affine[:3, 3] -= target_c[:3] - input_c[:3]\n\n    nii = Affine(\n        reference=nb.Nifti1Image(np.zeros(target_shape), target_affine, None),\n    ).apply(input_nii)\n    return nii\n\n\ndef resample_like(image, target, output_dtype=None, cval=0):\n    \"\"\"Resample the input image to be in the target's grid via identity transform.\"\"\"\n    from nitransforms.linear import Affine\n\n    return Affine(reference=target).apply(image, output_dtype=output_dtype, cval=cval)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "mriqc/synthstrip/model.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2022 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n# STATEMENT OF CHANGES: This file is derived from sources licensed under the FreeSurfer 1.0 license\n# terms, and this file has been changed.\n# The full licensing terms of the original work are found at:\n# https://github.com/freesurfer/freesurfer/blob/2995ded957961a7f3704de57eee88eb6cc30d52d/LICENSE.txt\n# A copy of the license has been archived in the ORIGINAL_LICENSE file\n# found within this redistribution.\n#\n# The original file this work derives from is found at:\n# https://github.com/freesurfer/freesurfer/blob/2995ded957961a7f3704de57eee88eb6cc30d52d/mri_synthstrip/mri_synthstrip\n#\n# [April 2022] CHANGES:\n#    * MAINT: Split the monolithic file into model and CLI submodules\n#    * ENH: Replace freesurfer Python bundle with in-house code.\n#\n\"\"\"\nRobust, universal skull-stripping for brain images of any type.\nIf you use SynthStrip in your analysis, please cite:\n\n  A Hoopes, JS Mora, AV Dalca, B Fischl, M Hoffmann.\n  SynthStrip: Skull-Stripping for Any Brain Image.\n  https://arxiv.org/abs/2203.09974\n\n\"\"\"\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\n\n\nclass StripModel(nn.Module):\n    def __init__(\n        self,\n        nb_features=16,\n        nb_levels=7,\n        feat_mult=2,\n        max_features=64,\n        nb_conv_per_level=2,\n        max_pool=2,\n        return_mask=False,\n    ):\n        super().__init__()\n\n        # dimensionality\n        ndims = 3\n\n        # build feature list automatically\n        if isinstance(nb_features, int):\n            if nb_levels is None:\n                raise ValueError('must provide unet nb_levels if nb_features is an integer')\n            feats = np.round(nb_features * feat_mult ** np.arange(nb_levels)).astype(int)\n            feats = np.clip(feats, 1, max_features)\n            nb_features = [\n                np.repeat(feats[:-1], nb_conv_per_level),\n                np.repeat(np.flip(feats), nb_conv_per_level),\n            ]\n        elif nb_levels is not None:\n            raise ValueError('cannot use nb_levels if nb_features is not an integer')\n\n        # extract any surplus (full resolution) decoder convolutions\n        enc_nf, dec_nf = nb_features\n        nb_dec_convs = len(enc_nf)\n        final_convs = dec_nf[nb_dec_convs:]\n        dec_nf = dec_nf[:nb_dec_convs]\n        self.nb_levels = int(nb_dec_convs / nb_conv_per_level) + 1\n\n        if isinstance(max_pool, int):\n            max_pool = [max_pool] * self.nb_levels\n\n        # cache downsampling / upsampling operations\n        MaxPooling = getattr(nn, f'MaxPool{ndims}d')\n        self.pooling = [MaxPooling(s) for s in max_pool]\n        self.upsampling = [nn.Upsample(scale_factor=s, mode='nearest') for s in max_pool]\n\n        # configure encoder (down-sampling path)\n        prev_nf = 1\n        encoder_nfs = [prev_nf]\n        self.encoder = nn.ModuleList()\n        for level in range(self.nb_levels - 1):\n            convs = nn.ModuleList()\n            for conv in range(nb_conv_per_level):\n                nf = enc_nf[level * nb_conv_per_level + conv]\n                convs.append(ConvBlock(ndims, prev_nf, nf))\n                prev_nf = nf\n            self.encoder.append(convs)\n            encoder_nfs.append(prev_nf)\n\n        # configure decoder (up-sampling path)\n        encoder_nfs = np.flip(encoder_nfs)\n        self.decoder = nn.ModuleList()\n        for level in range(self.nb_levels - 1):\n            convs = nn.ModuleList()\n            for conv in range(nb_conv_per_level):\n                nf = dec_nf[level * nb_conv_per_level + conv]\n                convs.append(ConvBlock(ndims, prev_nf, nf))\n                prev_nf = nf\n            self.decoder.append(convs)\n            if level < (self.nb_levels - 1):\n                prev_nf += encoder_nfs[level]\n\n        # now we take care of any remaining convolutions\n        self.remaining = nn.ModuleList()\n        for nf in final_convs:\n            self.remaining.append(ConvBlock(ndims, prev_nf, nf))\n            prev_nf = nf\n\n        # final convolutions\n        if return_mask:\n            self.remaining.append(ConvBlock(ndims, prev_nf, 2, activation=None))\n            self.remaining.append(nn.Softmax(dim=1))\n        else:\n            self.remaining.append(ConvBlock(ndims, prev_nf, 1, activation=None))\n\n    def forward(self, x):\n        # encoder forward pass\n        x_history = [x]\n        for level, convs in enumerate(self.encoder):\n            for conv in convs:\n                x = conv(x)\n            x_history.append(x)\n            x = self.pooling[level](x)\n\n        # decoder forward pass with upsampling and concatenation\n        for level, convs in enumerate(self.decoder):\n            for conv in convs:\n                x = conv(x)\n            if level < (self.nb_levels - 1):\n                x = self.upsampling[level](x)\n                x = torch.cat([x, x_history.pop()], dim=1)\n\n        # remaining convs at full resolution\n        for conv in self.remaining:\n            x = conv(x)\n\n        return x\n\n\nclass ConvBlock(nn.Module):\n    \"\"\"\n    Specific convolutional block followed by leakyrelu for unet.\n    \"\"\"\n\n    def __init__(self, ndims, in_channels, out_channels, stride=1, activation='leaky'):\n        super().__init__()\n\n        Conv = getattr(nn, f'Conv{ndims}d')\n        self.conv = Conv(in_channels, out_channels, 3, stride, 1)\n        if activation == 'leaky':\n            self.activation = nn.LeakyReLU(0.2)\n        elif activation is None:\n            self.activation = None\n        else:\n            raise ValueError(f'Unknown activation: {activation}')\n\n    def forward(self, x):\n        out = self.conv(x)\n        if self.activation is not None:\n            out = self.activation(out)\n        return out\n"
  },
  {
    "path": "mriqc/testing.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Utilities and mocks for testing and documentation building.\"\"\"\n\nfrom contextlib import contextmanager\nfrom pathlib import Path\nfrom tempfile import mkdtemp\n\nfrom niworkflows.data import Loader\nfrom toml import loads\n\n_load_data = Loader('mriqc')\n\n\n@contextmanager\ndef mock_config():\n    \"\"\"Create a mock config for documentation and testing purposes.\"\"\"\n    from . import config\n\n    filename = _load_data('data/config-example.toml')\n    settings = loads(filename.read_text())\n    for sectionname, configs in settings.items():\n        if sectionname != 'environment':\n            section = getattr(config, sectionname)\n            section.load(configs, init=False)\n    config.nipype.init()\n    config.loggers.init()\n\n    config.execution.work_dir = Path(mkdtemp())\n    config.execution.bids_dir = Path(_load_data('data/tests/ds000005')).absolute()\n    config.execution.init()\n\n    yield\n"
  },
  {
    "path": "mriqc/tests/test_config.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Exercise config module.\"\"\"\n\nfrom pathlib import Path\n\nimport pytest\n\n\ndef _expand_bids(tmp_path, testdata_path, testcase):\n    \"\"\"Expand manifest file into a temporal folder.\"\"\"\n\n    text = (testdata_path / f'{testcase}.manifest').read_text().splitlines()\n    root = Path(text[0].strip())\n    out_path = tmp_path / testcase\n\n    for path in reversed(text[1:]):\n        relpath = Path(path).relative_to(root)\n        if '.' in relpath.name or relpath in ('CHANGES', 'README', 'LICENSE'):\n            (out_path / relpath.parent).mkdir(parents=True, exist_ok=True)\n            if not (out_path / relpath).exists():\n                contents = '{}' if relpath.name.endswith('.json') else ''\n                (out_path / relpath).write_text(contents)\n        else:\n            (out_path / relpath).mkdir(parents=True, exist_ok=True)\n\n    if (\n        not (out_path / 'dataset_description.json').exists()\n        or 'Name' not in (out_path / 'dataset_description.json').read_text()\n    ):\n        (out_path / 'dataset_description.json').write_text(\n            '{\"Name\": \"Example dataset\", \"BIDSVersion\": \"1.0.2\"}'\n        )\n\n    return out_path\n\n\n@pytest.mark.parametrize(\n    'testcase',\n    [\n        'gh921-dmd-20220428-0',\n        'gh921-dmd-20230319-0',\n        'gh1086-ds004134',\n    ],\n)\ndef test_bids_indexing_manifest(tmp_path, testdata_path, testcase):\n    \"\"\"Check ``BIDSLayout`` is indexing what it should.\"\"\"\n\n    from importlib import reload\n\n    from mriqc import config\n\n    reload(config)\n\n    config.execution.output_dir = Path(tmp_path) / 'out'\n    config.execution.bids_dir = _expand_bids(\n        tmp_path,\n        testdata_path,\n        testcase,\n    )\n    config.execution.init()\n    assert len(config.execution.layout.get()) == int(\n        (testdata_path / f'{testcase}.oracle').read_text().strip()\n    )\n"
  },
  {
    "path": "mriqc/tests/test_main.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2024 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\nimport sys\n\nimport pytest\n\nfrom mriqc.__main__ import main\n\n\n@pytest.fixture(autouse=True)\ndef set_command(monkeypatch):\n    with monkeypatch.context() as m:\n        m.setattr(sys, 'argv', ['mriqc'])\n        yield\n\n\ndef test_help(capsys):\n    with pytest.raises(SystemExit):\n        main(['--help'])\n    captured = capsys.readouterr()\n    assert captured.out.startswith('usage: mriqc [-h]')\n\n\ndef test_main(tmp_path):\n    bids_dir = tmp_path / 'data/sub-01'\n    out_path = tmp_path / 'out'\n\n    with pytest.raises(SystemExit):\n        main([str(bids_dir), str(out_path)])\n\n    analysis_level = 'participant'\n    species = 'human'\n\n    with pytest.raises(SystemExit):\n        main(\n            [\n                str(bids_dir),\n                str(out_path),\n                analysis_level,\n                '--species',\n                species,\n            ]\n        )\n"
  },
  {
    "path": "mriqc/tests/test_parser.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2024 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\n\"\"\"Test parser.\"\"\"\n\nfrom pathlib import Path\n\nimport pytest\n\nfrom mriqc.cli.parser import _build_parser\n\nMIN_ARGS = ['bids_dir', 'out', 'participant']\n\n\ndef _create_set_bids_dir(args, _tmp_path):\n    datapath = _tmp_path / MIN_ARGS[0]\n    datapath.mkdir(exist_ok=True)\n    args[0] = str(datapath)\n    return args\n\n\n@pytest.mark.parametrize(\n    ('args', 'code'),\n    [\n        ([], 2),\n    ],\n)\ndef test_parser_errors(args, code):\n    \"\"\"Check behavior of the parser.\"\"\"\n    with pytest.raises(SystemExit) as error:\n        _build_parser().parse_args(args)\n\n    assert error.value.code == code\n\n\n@pytest.mark.parametrize(\n    'args',\n    [\n        MIN_ARGS,\n    ],\n)\ndef test_parser_valid(tmp_path, args):\n    \"\"\"Check valid arguments.\"\"\"\n    args = _create_set_bids_dir(args, tmp_path)\n\n    opts = _build_parser().parse_args(args)\n\n    assert opts.bids_dir == Path(args[0])\n\n\n@pytest.mark.parametrize(\n    ('argdest', 'argstr', 'argval'),\n    [\n        ('verbose_count', '-v', 1),\n        ('verbose_count', '-vv', 2),\n        ('verbose_count', '-vvv', 3),\n    ],\n)\ndef test_verbosity_arg(tmp_path, argdest, argstr, argval):\n    \"\"\"Check the correct parsing of the verbosity argument.\"\"\"\n    args = MIN_ARGS + [argstr]\n\n    args = _create_set_bids_dir(args, tmp_path)\n\n    opts = _build_parser().parse_args(args)\n\n    assert getattr(opts, argdest) == argval\n\n\n@pytest.mark.parametrize(\n    ('argval', '_species'),\n    [\n        ('human', 'human'),\n        ('rat', 'rat'),\n    ],\n)\ndef test_species_arg(tmp_path, argval, _species):\n    \"\"\"Check the correct parsing of the species argument.\"\"\"\n    args = MIN_ARGS + ['--species', argval]\n\n    args = _create_set_bids_dir(args, tmp_path)\n\n    opts = _build_parser().parse_args(args)\n\n    assert opts.species == _species\n"
  },
  {
    "path": "mriqc/tests/test_reports.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Exercise report generation with *NiReports*.\"\"\"\n\nfrom json import loads\nfrom pathlib import Path\n\nimport pytest\nfrom nireports.assembler.report import Report\n\n\n@pytest.mark.parametrize(\n    ('dataset', 'subject'),\n    [\n        ('ds002785', '0017'),\n        ('ds002785', '0042'),\n    ],\n)\ndef test_anat_reports(tmp_path, testdata_path, outdir, dataset, subject):\n    \"\"\"Generate anatomical reports.\"\"\"\n\n    outdir = outdir if outdir is not None else tmp_path\n    mriqc_data = Path(__file__).parent.parent / 'data'\n    reportlets_dir = mriqc_data / 'tests' / dataset\n\n    mriqc_json = loads(\n        (reportlets_dir / f'sub-{subject}' / 'anat' / f'sub-{subject}_T1w.json').read_text()\n    )\n\n    Report(\n        outdir,\n        bootstrap_file=mriqc_data / 'bootstrap-anat.yml',\n        run_uuid='some-id',\n        reportlets_dir=reportlets_dir,\n        subject=subject,\n        suffix='T1w',\n        metadata={\n            'dataset': dataset,\n            'about-metadata': {\n                'Provenance Information': mriqc_json.pop('provenance'),\n                'Dataset Information': mriqc_json.pop('bids_meta'),\n                'Extracted Image quality metrics (IQMs)': mriqc_json,\n            },\n        },\n        plugin_meta={\n            'filename': f'sub-{subject}_T1w.nii.gz',\n            'dataset': dataset,\n        },\n    ).generate_report()\n"
  },
  {
    "path": "mriqc/utils/__init__.py",
    "content": ""
  },
  {
    "path": "mriqc/utils/bids.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"PyBIDS tooling.\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport os\nfrom pathlib import Path\n\nDOI = 'https://doi.org/10.1371/journal.pone.0184661'\n\n\ndef write_bidsignore(deriv_dir):\n    from mriqc.config import SUPPORTED_SUFFIXES\n\n    bids_ignore = [\n        '*.html',\n        'logs/',  # Reports\n    ] + [f'*_{suffix}.json' for suffix in SUPPORTED_SUFFIXES]\n\n    ignore_file = Path(deriv_dir) / '.bidsignore'\n\n    ignore_file.write_text('\\n'.join(bids_ignore) + '\\n')\n\n\ndef write_derivative_description(bids_dir, deriv_dir):\n    from mriqc import __download__, __version__\n\n    bids_dir = Path(bids_dir)\n    deriv_dir = Path(deriv_dir)\n    desc = {\n        'BIDSVersion': '1.4.0',\n        'DatasetType': 'derivative',\n        'GeneratedBy': [\n            {\n                'Name': 'MRIQC',\n                'Version': __version__,\n                'CodeURL': __download__,\n            }\n        ],\n        'HowToAcknowledge': f'Please cite our paper ({DOI}).',\n    }\n\n    # Keys that can only be set by environment\n    # XXX: This currently has no effect, but is a stand-in to remind us to figure out\n    # how to detect the container\n    if 'MRIQC_DOCKER_TAG' in os.environ:\n        desc['GeneratedBy'][0]['Container'] = {\n            'Type': 'docker',\n            'Tag': f'nipreps/mriqc:{os.environ[\"MRIQC_DOCKER_TAG\"]}',\n        }\n    if 'MRIQC_SINGULARITY_URL' in os.environ:\n        desc['GeneratedBy'][0]['Container'] = {\n            'Type': 'singularity',\n            'URI': os.getenv('MRIQC_SINGULARITY_URL'),\n        }\n\n    # Keys deriving from source dataset\n    orig_desc = {}\n    fname = bids_dir / 'dataset_description.json'\n    if fname.exists():\n        orig_desc = json.loads(fname.read_text())\n\n    if 'Name' in orig_desc:\n        desc['Name'] = f'MRIQC - {orig_desc[\"Name\"]}'\n    else:\n        desc['Name'] = 'MRIQC - MRI Quality Control'\n\n    if 'DatasetDOI' in orig_desc:\n        desc['SourceDatasets'] = [\n            {\n                'URL': f'https://doi.org/{orig_desc[\"DatasetDOI\"]}',\n                'DOI': orig_desc['DatasetDOI'],\n            }\n        ]\n    if 'License' in orig_desc:\n        desc['License'] = orig_desc['License']\n\n    Path.write_text(deriv_dir / 'dataset_description.json', json.dumps(desc, indent=4))\n\n\ndef derive_bids_fname(\n    orig_path: str | Path,\n    entity: str | None = None,\n    newsuffix: str | None = None,\n    newpath: str | Path | None = None,\n    newext: str | None = None,\n    position: int = -1,\n    absolute: bool = True,\n) -> Path | str:\n    \"\"\"\n    Derive a new file name from a BIDS-formatted path.\n\n    Parameters\n    ----------\n    orig_path : :obj:`str` or :obj:`os.pathlike`\n        A filename (may or may not include path).\n    entity : :obj:`str`, optional\n        A new BIDS-like key-value pair.\n    newsuffix : :obj:`str`, optional\n        Replace the BIDS suffix.\n    newpath : :obj:`str` or :obj:`os.pathlike`, optional\n        Path to replace the path of the input orig_path.\n    newext : :obj:`str`, optional\n        Replace the extension of the file.\n    position : :obj:`int`, optional\n        Position to insert the entity in the filename.\n    absolute : :obj:`bool`, optional\n        If True (default), returns the absolute path of the modified filename.\n\n    Returns\n    -------\n    Absolute path of the modified filename\n\n    Examples\n    --------\n    >>> derive_bids_fname(\n    ...     'sub-001/ses-01/anat/sub-001_ses-01_T1w.nii.gz',\n    ...     entity='desc-preproc',\n    ...     absolute=False,\n    ... )\n    PosixPath('sub-001/ses-01/anat/sub-001_ses-01_desc-preproc_T1w.nii.gz')\n\n    >>> derive_bids_fname(\n    ...     'sub-001/ses-01/anat/sub-001_ses-01_T1w.nii.gz',\n    ...     entity='desc-brain',\n    ...     newsuffix='mask',\n    ...     newext=\".nii\",\n    ...     absolute=False,\n    ... )  # doctest: +ELLIPSIS\n    PosixPath('sub-001/ses-01/anat/sub-001_ses-01_desc-brain_mask.nii')\n\n    >>> derive_bids_fname(\n    ...     'sub-001/ses-01/anat/sub-001_ses-01_T1w.nii.gz',\n    ...     entity='desc-brain',\n    ...     newsuffix='mask',\n    ...     newext=\".nii\",\n    ...     newpath=\"/output/node\",\n    ...     absolute=True,\n    ... )  # doctest: +ELLIPSIS\n    PosixPath('/output/node/sub-001_ses-01_desc-brain_mask.nii')\n\n    >>> derive_bids_fname(\n    ...     'sub-001/ses-01/anat/sub-001_ses-01_T1w.nii.gz',\n    ...     entity='desc-brain',\n    ...     newsuffix='mask',\n    ...     newext=\".nii\",\n    ...     newpath=\".\",\n    ...     absolute=False,\n    ... )  # doctest: +ELLIPSIS\n    PosixPath('sub-001_ses-01_desc-brain_mask.nii')\n\n    \"\"\"\n\n    orig_path = Path(orig_path)\n    newpath = orig_path.parent if newpath is None else Path(newpath)\n\n    ext = ''.join(orig_path.suffixes)\n    newext = newext if newext is not None else ext\n    orig_stem = orig_path.name.replace(ext, '')\n\n    suffix = orig_stem.rsplit('_', maxsplit=1)[-1].strip('_')\n    newsuffix = newsuffix.strip('_') if newsuffix is not None else suffix\n\n    orig_stem = orig_stem.replace(suffix, '').strip('_')\n    bidts = [bit for bit in orig_stem.split('_') if bit]\n    if entity:\n        if position == -1:\n            bidts.append(entity)\n        else:\n            bidts.insert(position, entity.strip('_'))\n\n    retval = newpath / f'{\"_\".join(bidts)}_{newsuffix}.{newext.strip(\".\")}'\n\n    return retval.absolute() if absolute else retval\n"
  },
  {
    "path": "mriqc/utils/debug.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n# STATEMENT OF CHANGES: This file is derived from sources licensed under the Apache-2.0 terms,\n# and uses the following portion of the original code:\n# https://github.com/dandi/dandi-cli/blob/da3b7a726c4a352dfb53a0c6bee59e660de827e6/dandi/utils.py#L49-L82\n#\n#\n# ORIGINAL WORK'S ATTRIBUTION NOTICE:\n#\n#    Copyright 2020 DANDI Client Developers\n#\n#    Licensed under the Apache License, Version 2.0 (the \"License\");\n#    you may not use this file except in compliance with the License.\n#    You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#    Unless required by applicable law or agreed to in writing, software\n#    distributed under the License is distributed on an \"AS IS\" BASIS,\n#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#    See the License for the specific language governing permissions and\n#    limitations under the License.\n#\nimport sys\n\n\ndef is_interactive():\n    \"\"\"Return True if all in/outs are tty\"\"\"\n    # TODO: check on windows if hasattr check would work correctly and add value:\n    #\n    return sys.stdin.isatty() and sys.stdout.isatty() and sys.stderr.isatty()\n\n\ndef setup_exceptionhook(ipython=False):\n    \"\"\"Overloads default sys.excepthook with our exceptionhook handler.\n    If interactive, our exceptionhook handler will invoke\n    pdb.post_mortem; if not interactive, then invokes default handler.\n    \"\"\"\n\n    def _pdb_excepthook(exc_type, value, tb):\n        import traceback\n\n        traceback.print_exception(exc_type, value, tb)\n        print()\n        if is_interactive():\n            import pdb  # noqa: T100\n\n            pdb.post_mortem(tb)\n\n    if ipython:\n        from IPython.core import ultratb\n\n        sys.excepthook = ultratb.FormattedTB(\n            mode='Verbose',\n            # color_scheme='Linux',\n            call_pdb=is_interactive(),\n        )\n    else:\n        sys.excepthook = _pdb_excepthook\n"
  },
  {
    "path": "mriqc/utils/misc.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Helper functions.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nimport json\nfrom collections import OrderedDict\nfrom collections.abc import Iterable\nfrom functools import partial\nfrom os import cpu_count\nfrom pathlib import Path\nfrom typing import Callable, TypeVar\n\nimport nibabel as nb\nimport numpy as np\nimport pandas as pd\n\ntry:\n    from collections.abc import MutableMapping\nexcept ImportError:\n    from collections.abc import MutableMapping\n\nR = TypeVar('R')\n\nIMTYPES = {\n    'T1w': 'anat',\n    'T2w': 'anat',\n    'bold': 'func',\n    'dwi': 'dwi',\n}\n\nBIDS_COMP = OrderedDict(\n    [\n        ('subject_id', 'sub'),\n        ('session_id', 'ses'),\n        ('task_id', 'task'),\n        ('acq_id', 'acq'),\n        ('rec_id', 'rec'),\n        ('run_id', 'run'),\n    ]\n)\n\nBIDS_EXPR = \"\"\"\\\n^sub-(?P<subject_id>[a-zA-Z0-9]+)(_ses-(?P<session_id>[a-zA-Z0-9]+))?\\\n(_task-(?P<task_id>[a-zA-Z0-9]+))?(_acq-(?P<acq_id>[a-zA-Z0-9]+))?\\\n(_rec-(?P<rec_id>[a-zA-Z0-9]+))?(_run-(?P<run_id>[a-zA-Z0-9]+))?\\\n\"\"\"\n\n\nasync def worker(job: Callable[[], R], semaphore) -> R:\n    async with semaphore:\n        loop = asyncio.get_running_loop()\n        return await loop.run_in_executor(None, job)\n\n\ndef reorder_csv(csv_file, out_file=None):\n    \"\"\"\n    Put subject, session and scan in front of csv file\n\n    :param str csv_file: the input csv file\n    :param str out_file: if provided, a new csv file is created\n\n    :return: the path to the file with the columns reordered\n\n\n    \"\"\"\n    if isinstance(csv_file, list):\n        csv_file = csv_file[-1]\n\n    if out_file is None:\n        out_file = csv_file\n\n    dataframe = pd.read_csv(csv_file)\n    cols = dataframe.columns.tolist()  # pylint: disable=no-member\n    try:\n        cols.remove('Unnamed: 0')\n    except ValueError:\n        # The column does not exist\n        pass\n\n    for col in ('scan', 'session', 'subject'):\n        cols.remove(col)\n        cols.insert(0, col)\n\n    dataframe[cols].to_csv(out_file)\n    return out_file\n\n\ndef rotate_files(fname):\n    \"\"\"A function to rotate file names\"\"\"\n    import glob\n    import os\n    import os.path as op\n\n    name, ext = op.splitext(fname)\n    if ext == '.gz':\n        name, ext2 = op.splitext(fname)\n        ext = ext2 + ext\n\n    if not op.isfile(fname):\n        return\n\n    prev = glob.glob(f'{name}.*{ext}')\n    prev.insert(0, fname)\n    prev.append(f'{name}.{len(prev) - 1:d}{ext}')\n    for i in reversed(list(range(1, len(prev)))):\n        os.rename(prev[i - 1], prev[i])\n\n\ndef bids_path(subid, sesid=None, runid=None, prefix=None, out_path=None, ext='json'):\n    import os.path as op\n\n    fname = f'{subid}'\n    if prefix is not None:\n        if not prefix.endswith('_'):\n            prefix += '_'\n        fname = prefix + fname\n    if sesid is not None:\n        fname += f'_ses-{sesid}'\n    if runid is not None:\n        fname += f'_run-{runid}'\n\n    if out_path is not None:\n        fname = op.join(out_path, fname)\n    return op.abspath(fname + '.' + ext)\n\n\ndef generate_pred(derivatives_dir, output_dir, mod):\n    \"\"\"\n    Reads the metadata in the JIQM (json iqm) files and\n    generates a corresponding prediction CSV table\n    \"\"\"\n\n    if mod != 'T1w':\n        return None\n\n    # If some were found, generate the CSV file and group report\n    jsonfiles = list(output_dir.glob(f'sub-*/**/{IMTYPES[mod]}/sub-*_{mod}.json'))\n    if not jsonfiles:\n        return None\n\n    headers = list(BIDS_COMP) + ['mriqc_pred']\n    predictions = {k: [] for k in headers}\n\n    for jsonfile in jsonfiles:\n        with open(jsonfile) as jsondata:\n            data = json.load(jsondata).pop('bids_meta', None)\n\n        if data is None:\n            continue\n\n        for k in headers:\n            predictions[k].append(data.pop(k, None))\n\n    dataframe = pd.DataFrame(predictions).sort_values(by=list(BIDS_COMP))\n\n    # Drop empty columns\n    dataframe.dropna(axis='columns', how='all', inplace=True)\n\n    bdits_cols = list(set(BIDS_COMP) & set(dataframe.columns.ravel()))\n\n    # Drop duplicates\n    dataframe.drop_duplicates(bdits_cols, keep='last', inplace=True)\n\n    out_csv = Path(output_dir) / f'{mod}_predicted_qa_csv'\n    dataframe[bdits_cols + ['mriqc_pred']].to_csv(str(out_csv), index=False)\n    return out_csv\n\n\ndef generate_tsv(output_dir, mod):\n    \"\"\"\n    Generates a tsv file from all json files in the derivatives directory\n    \"\"\"\n\n    # If some were found, generate the CSV file and group report\n    out_tsv = output_dir / (f'group_{mod}.tsv')\n    jsonfiles = list(output_dir.glob(f'sub-*/**/{IMTYPES[mod]}/sub-*_{mod}.json'))\n    if not jsonfiles:\n        return None, out_tsv\n\n    datalist = []\n    for jsonfile in jsonfiles:\n        dfentry = _read_and_save(jsonfile)\n\n        if dfentry is not None:\n            bids_name = str(Path(jsonfile.name).stem)\n            dfentry.pop('bids_meta', None)\n            dfentry.pop('provenance', None)\n            dfentry['bids_name'] = bids_name\n            datalist.append(dfentry)\n\n    dataframe = pd.DataFrame(datalist)\n    cols = dataframe.columns.tolist()  # pylint: disable=no-member\n    dataframe = dataframe.sort_values(by=['bids_name'])\n\n    # Drop duplicates\n    dataframe.drop_duplicates(['bids_name'], keep='last', inplace=True)\n\n    # Set filename at front\n    cols.insert(0, cols.pop(cols.index('bids_name')))\n    dataframe[cols].to_csv(str(out_tsv), index=False, sep='\\t')\n    return dataframe, out_tsv\n\n\ndef _read_and_save(in_file):\n    return json.loads(Path(in_file).read_text()) or None\n\n\ndef _flatten(in_dict, parent_key='', sep='_'):\n    items = []\n    for k, val in list(in_dict.items()):\n        new_key = parent_key + sep + k if parent_key else k\n        if isinstance(val, MutableMapping):\n            items.extend(list(_flatten(val, new_key, sep=sep).items()))\n        else:\n            items.append((new_key, val))\n    return dict(items)\n\n\ndef _flatten_dict(indict):\n    out_qc = {}\n    for k, value in list(indict.items()):\n        if not isinstance(value, dict):\n            out_qc[k] = value\n        else:\n            for subk, subval in list(value.items()):\n                if not isinstance(subval, dict):\n                    out_qc['_'.join([k, subk])] = subval\n                else:\n                    for ssubk, ssubval in list(subval.items()):\n                        out_qc['_'.join([k, subk, ssubk])] = ssubval\n    return out_qc\n\n\ndef _flatten_list(xs):\n    for x in xs:\n        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):\n            yield from _flatten_list(x)\n        else:\n            yield x\n\n\ndef _datalad_get(input_list, nprocs=None):\n    from mriqc import config\n\n    if not config.execution.bids_dir_datalad or not config.execution.datalad_get:\n        return\n\n    # Delay datalad import until we're sure we'll need it\n    import logging\n\n    from datalad.api import get\n\n    _dataladlog = logging.getLogger('datalad')\n    _dataladlog.setLevel(logging.WARNING)\n\n    config.loggers.cli.log(\n        25, 'DataLad dataset identified, attempting to `datalad get` unavailable files.'\n    )\n    return get(\n        list(_flatten_list(input_list)),\n        dataset=str(config.execution.bids_dir),\n        jobs=nprocs\n        if not None\n        else max(\n            config.nipype.omp_nthreads,\n            config.nipype.nprocs,\n        ),\n    )\n\n\ndef _file_meta_and_size(\n    files: list | str,\n    volmin: int | None = 1,\n    volmax: int | None = None,\n):\n    \"\"\"\n    Identify the largest file size (allows multi-echo groups).\n\n    Parameters\n    ----------\n    files : :obj:`list`\n        List of :obj:`os.pathlike` or sublist of :obj:`os.pathlike` (multi-echo case)\n        of files to be extracted.\n    volmin : :obj:`int`\n        Minimum number of volumes that inputs must have.\n    volmax : :obj:`int`\n        Maximum number of volumes that inputs must have.\n\n    Returns\n    -------\n    :obj:`tuple`\n        A tuple (metadata, entities, sizes, valid) of items containing the different\n        aspects extracted from the input(s).\n\n    \"\"\"\n\n    import os\n\n    from mriqc import config\n\n    multifile = isinstance(files, (list, tuple))\n    if multifile:\n        metadata = []\n        _bids_list = []\n        _size_list = []\n        _valid_list = []\n\n        for filename in files:\n            metadata_i, entities_i, sizes_i, valid_i = _file_meta_and_size(\n                filename,\n                volmin=volmin,\n                volmax=volmax,\n            )\n\n            # Add to output lists\n            metadata.append(metadata_i)\n            _bids_list.append(entities_i)\n            _size_list.append(sizes_i)\n            _valid_list.append(valid_i)\n\n        valid = all(_valid_list) and len({_m['NumberOfVolumes'] for _m in metadata}) == 1\n        return metadata, _merge_entities(_bids_list), np.sum(_size_list), valid\n\n    metadata = config.execution.layout.get_metadata(files)\n    entities = config.execution.layout.parse_file_entities(files)\n    size = os.path.getsize(files) / (1024**3)\n\n    metadata['FileSize'] = size\n    metadata['FileSizeUnits'] = 'GB'\n\n    try:\n        nii = nb.load(files)\n        nifti_len = nii.shape[3]\n    except nb.filebasedimages.ImageFileError:\n        nifti_len = None\n    except IndexError:  # shape has only 3 elements\n        nifti_len = 1 if nii.dataobj.ndim == 3 else -1\n\n    valid = True\n    if volmin is not None:\n        valid = nifti_len >= volmin\n\n    if valid and volmax is not None:\n        valid = nifti_len <= volmax\n\n    metadata['NumberOfVolumes'] = nifti_len\n\n    return metadata, entities, size, valid\n\n\nasync def _extract_meta_and_size(\n    filelist: list,\n    volmin: int | None = 1,\n    volmax: int | None = None,\n    max_concurrent: int = min(cpu_count(), 12),\n) -> tuple[list, list, list, list]:\n    \"\"\"\n    Extract corresponding metadata and file size in GB.\n\n    Parameters\n    ----------\n    filelist : :obj:`list`\n        List of :obj:`os.pathlike` or sublist of :obj:`os.pathlike` (multi-echo case)\n        of files to be extracted.\n    volmin : :obj:`int`\n        Minimum number of volumes that inputs must have.\n    volmax : :obj:`int`\n        Maximum number of volumes that inputs must have.\n    max_concurrent : :obj:`int`\n        Maximum number of concurrent coroutines (files or multi-echo sets).\n\n    Returns\n    -------\n    :obj:`tuple`\n        A tuple (metadata, entities, sizes, valid) of lists containing the different\n        aspects extracted from inputs.\n\n    \"\"\"\n\n    semaphore = asyncio.Semaphore(max_concurrent)\n    tasks = []\n    for filename in filelist:\n        tasks.append(\n            asyncio.create_task(\n                worker(\n                    partial(\n                        _file_meta_and_size,\n                        filename,\n                        volmin=volmin,\n                        volmax=volmax,\n                    ),\n                    semaphore,\n                )\n            )\n        )\n\n    # Gather guarantees the order of the output\n    metadata, entities, sizes, valid = list(zip(*await asyncio.gather(*tasks)))\n    return metadata, entities, sizes, valid\n\n\ndef initialize_meta_and_data(\n    max_concurrent: int = min(cpu_count(), 12),\n) -> None:\n    \"\"\"\n    Mine data and metadata corresponding to the dataset.\n\n    Get files if datalad enabled and extract the necessary metadata.\n\n    Parameters\n    ----------\n    max_concurrent : :obj:`int`\n        Maximum number of concurrent coroutines (files or multi-echo sets).\n\n    Returns\n    -------\n    :obj:`None`\n\n    \"\"\"\n    from mriqc import config\n\n    # Datalad-get all files\n    dataset = config.workflow.inputs.values()\n    _datalad_get(dataset)\n\n    # Extract metadata and filesize\n    config.workflow.inputs_metadata = {}\n    config.workflow.inputs_entities = {}\n    config.workflow.biggest_file_gb = {}\n    for mod, input_list in config.workflow.inputs.items():\n        config.loggers.cli.log(\n            25,\n            f'Extracting metadata and entities for {len(input_list)} input runs '\n            f\"of modality '{mod}'...\",\n        )\n\n        # Some modalities require a minimum number of volumes\n        volmin = None\n        if mod == 'bold':\n            volmin = config.workflow.min_len_bold\n        elif mod == 'dwi':\n            volmin = config.workflow.min_len_dwi\n\n        # Some modalities require a maximum number of volumes\n        volmax = None\n        if mod in ('T1w', 'T2w'):\n            volmax = 1\n\n        # Run extraction in a asyncio coroutine loop\n        metadata, entities, size, valid = asyncio.run(\n            _extract_meta_and_size(\n                input_list,\n                max_concurrent=max_concurrent,\n                volmin=volmin,\n                volmax=volmax,\n            )\n        )\n\n        # Identify nonconformant files that need to be dropped (and drop them)\n        if num_dropped := len(input_list) - np.sum(valid):\n            config.loggers.workflow.warn(\n                f'{num_dropped} cannot be processed (too short or too long)'\n            )\n\n            filtered_results = [\n                _v[:-1]\n                for _v in zip(input_list, metadata, entities, size, valid)\n                if _v[-1] is True\n            ]\n            input_list, metadata, entities, size = list(zip(*filtered_results))\n            config.workflow.inputs[mod] = input_list\n\n        # Finalizing (write to config so that values are propagated)\n        _max_size = np.max(size)\n        config.workflow.inputs_metadata[mod] = metadata\n        config.workflow.inputs_entities[mod] = entities\n        config.workflow.biggest_file_gb[mod] = float(_max_size)  # Cast required to store YAML\n\n        config.loggers.cli.log(\n            25,\n            f\"File size ('{mod}'): {_max_size:.2f}|{np.mean(size):.2f} GB [maximum|average].\",\n        )\n\n\ndef _merge_entities(\n    entities: list,\n) -> dict:\n    \"\"\"\n    Merge a list of dictionaries with entities dropping those with nonuniform values.\n\n    Examples\n    --------\n    >>> _merge_entities([\n    ...     {'subject': '001', 'session': '001'},\n    ...     {'subject': '001', 'session': '002'},\n    ... ])\n    {'subject': '001'}\n\n    >>> _merge_entities([\n    ...     {'subject': '001', 'session': '002'},\n    ...     {'subject': '001', 'session': '002'},\n    ... ])\n    {'subject': '001', 'session': '002'}\n\n    >>> _merge_entities([\n    ...     {'subject': '001', 'session': '002'},\n    ...     {'subject': '001', 'session': '002', 'run': 1},\n    ... ])\n    {'subject': '001', 'session': '002'}\n\n    >>> _merge_entities([\n    ...     {'subject': '001', 'session': '002'},\n    ...     {'subject': '001', 'run': 1},\n    ... ])\n    {'subject': '001'}\n\n    \"\"\"\n    out_entities = {}\n\n    bids_keys = set(entities[0].keys())\n    for entities_i in entities[1:]:\n        bids_keys.intersection_update(entities_i.keys())\n\n    # Preserve ordering\n    bids_keys = [_b for _b in entities[0].keys() if _b in bids_keys]\n\n    for key in bids_keys:\n        values = {_entities[key] for _entities in entities}\n        if len(values) == 1:\n            out_entities[key] = values.pop()\n\n    return out_entities\n"
  },
  {
    "path": "mriqc/utils/telemetry.py",
    "content": "import migas\n\nfrom .. import __version__, config\n\n\ndef setup_migas(init_ping: bool = True, exit_ping: bool = True) -> None:\n    \"\"\"\n    Prepare the migas python client to communicate with a migas server.\n    If ``init`` is ``True``, send an initial breadcrumb.\n    \"\"\"\n    # generate session UUID from generated run UUID\n    session_id = None\n    if config.execution.run_uuid:\n        session_id = config.execution.run_uuid.split('_', 1)[-1]\n\n    migas.setup(session_id=session_id)\n    if init_ping:\n        # send initial status ping\n        send_crumb(status='R', status_desc='workflow start')\n    if exit_ping:\n        from migas.error.nipype import node_execution_error\n\n        migas.track_exit(\n            'nipreps/mriqc',\n            __version__,\n            {'NodeExecutionError': node_execution_error},\n        )\n\n\ndef send_crumb(**kwargs) -> dict:\n    \"\"\"\n    Communicate with the migas telemetry server. This requires `migas.setup()` to be called.\n    \"\"\"\n    return migas.add_breadcrumb('nipreps/mriqc', __version__, **kwargs)\n"
  },
  {
    "path": "mriqc/workflows/__init__.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\n.. automodule:: mriqc.workflows.anatomical\n    :members:\n    :undoc-members:\n    :show-inheritance:\n\n\n.. automodule:: mriqc.workflows.functional\n    :members:\n    :undoc-members:\n    :show-inheritance:\n\n\"\"\"\n\nfrom mriqc.workflows.anatomical.base import anat_qc_workflow\nfrom mriqc.workflows.functional.base import fmri_qc_workflow\n\n__all__ = [\n    'anat_qc_workflow',\n    'fmri_qc_workflow',\n]\n"
  },
  {
    "path": "mriqc/workflows/anatomical/__init__.py",
    "content": ""
  },
  {
    "path": "mriqc/workflows/anatomical/base.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nAnatomical workflow\n===================\n\n.. image :: _static/anatomical_workflow_source.svg\n\nThe anatomical workflow follows the following steps:\n\n#. Conform (reorientations, revise data types) input data and read\n   associated metadata.\n#. Skull-stripping (AFNI).\n#. Calculate head mask -- :py:func:`headmsk_wf`.\n#. Spatial Normalization to MNI (ANTs)\n#. Calculate air mask above the nasial-cerebelum plane -- :py:func:`airmsk_wf`.\n#. Brain tissue segmentation (FAST).\n#. Extraction of IQMs -- :py:func:`compute_iqms`.\n#. Individual-reports generation --\n   :py:func:`~mriqc.workflows.anatomical.output.init_anat_report_wf`.\n\nThis workflow is orchestrated by :py:func:`anat_qc_workflow`.\n\nFor the skull-stripping, we use ``afni_wf`` from ``niworkflows.anat.skullstrip``:\n\n.. workflow::\n\n    from niworkflows.anat.skullstrip import afni_wf\n    from mriqc.testing import mock_config\n    with mock_config():\n        wf = afni_wf()\n\n\"\"\"\n\nfrom itertools import chain\n\nfrom nipype.interfaces import utility as niu\nfrom nipype.pipeline import engine as pe\nfrom niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms\nfrom templateflow.api import get as get_template\n\nfrom mriqc import config\nfrom mriqc.interfaces import (\n    ArtifactMask,\n    ComputeQI2,\n    ConformImage,\n    IQMFileSink,\n    RotationMask,\n    StructuralQC,\n)\nfrom mriqc.interfaces.reports import AddProvenance\nfrom mriqc.messages import BUILDING_WORKFLOW\nfrom mriqc.workflows.anatomical.output import init_anat_report_wf\nfrom mriqc.workflows.utils import get_fwhmx\n\n\ndef anat_qc_workflow(name='anatMRIQC'):\n    \"\"\"\n    One-subject-one-session-one-run pipeline to extract the NR-IQMs from\n    anatomical images\n\n    .. workflow::\n\n        import os.path as op\n        from mriqc.workflows.anatomical.base import anat_qc_workflow\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = anat_qc_workflow()\n\n    \"\"\"\n    from mriqc.workflows.shared import synthstrip_wf\n\n    # Enable if necessary\n    # mem_gb = max(\n    #     config.workflow.biggest_file_gb['t1w'],\n    #     config.workflow.biggest_file_gb['t2w'],\n    # )\n    dataset = list(\n        chain(\n            config.workflow.inputs.get('t1w', []),\n            config.workflow.inputs.get('t2w', []),\n        )\n    )\n    metadata = list(\n        chain(\n            config.workflow.inputs_metadata.get('t1w', []),\n            config.workflow.inputs_metadata.get('t2w', []),\n        )\n    )\n    entities = list(\n        chain(\n            config.workflow.inputs_entities.get('t1w', []),\n            config.workflow.inputs_entities.get('t2w', []),\n        )\n    )\n    message = BUILDING_WORKFLOW.format(\n        modality='anatomical',\n        detail=f'for {len(dataset)} NIfTI files.',\n    )\n    config.loggers.workflow.info(message)\n\n    # Initialize workflow\n    workflow = pe.Workflow(name=name)\n\n    # Define workflow, inputs and outputs\n    # 0. Get data\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=['in_file', 'metadata', 'entities'],\n        ),\n        name='inputnode',\n    )\n    inputnode.synchronize = True  # Do not test combinations of iterables\n    inputnode.iterables = [\n        ('in_file', dataset),\n        ('metadata', metadata),\n        ('entities', entities),\n    ]\n\n    outputnode = pe.Node(niu.IdentityInterface(fields=['out_json']), name='outputnode')\n\n    # 1. Reorient anatomical image\n    to_ras = pe.Node(ConformImage(check_dtype=False), name='conform')\n    # 2. species specific skull-stripping\n    if config.workflow.species.lower() == 'human':\n        skull_stripping = synthstrip_wf(omp_nthreads=config.nipype.omp_nthreads)\n        ss_bias_field = 'outputnode.bias_image'\n    else:\n        from nirodents.workflows.brainextraction import init_rodent_brain_extraction_wf\n\n        skull_stripping = init_rodent_brain_extraction_wf(template_id=config.workflow.template_id)\n        ss_bias_field = 'final_n4.bias_image'\n    # 3. Head mask\n    hmsk = headmsk_wf(omp_nthreads=config.nipype.omp_nthreads)\n    # 4. Spatial Normalization, using ANTs\n    norm = spatial_normalization()\n    # 5. Air mask (with and without artifacts)\n    amw = airmsk_wf()\n    # 6. Brain tissue segmentation\n    bts = init_brain_tissue_segmentation()\n    # 7. Compute IQMs\n    iqmswf = compute_iqms()\n    # Reports\n    anat_report_wf = init_anat_report_wf()\n\n    # Connect all nodes\n    # fmt: off\n    workflow.connect([\n        (inputnode, anat_report_wf, [\n            ('in_file', 'inputnode.name_source'),\n        ]),\n        (inputnode, to_ras, [('in_file', 'in_file')]),\n        (inputnode, iqmswf, [('in_file', 'inputnode.in_file'),\n                             ('metadata', 'inputnode.metadata'),\n                             ('entities', 'inputnode.entities')]),\n        (inputnode, norm, [(('in_file', _get_mod), 'inputnode.modality')]),\n        (to_ras, skull_stripping, [('out_file', 'inputnode.in_files')]),\n        (skull_stripping, hmsk, [\n            ('outputnode.out_corrected', 'inputnode.in_file'),\n            ('outputnode.out_mask', 'inputnode.brainmask'),\n        ]),\n        (skull_stripping, bts, [('outputnode.out_mask', 'inputnode.brainmask')]),\n        (skull_stripping, norm, [\n            ('outputnode.out_corrected', 'inputnode.moving_image'),\n            ('outputnode.out_mask', 'inputnode.moving_mask')]),\n        (norm, bts, [('outputnode.out_tpms', 'inputnode.std_tpms')]),\n        (norm, amw, [\n            ('outputnode.ind2std_xfm', 'inputnode.ind2std_xfm')]),\n        (norm, iqmswf, [\n            ('outputnode.out_tpms', 'inputnode.std_tpms')]),\n        (norm, anat_report_wf, ([\n            ('outputnode.out_report', 'inputnode.mni_report')])),\n        (norm, hmsk, [('outputnode.out_tpms', 'inputnode.in_tpms')]),\n        (to_ras, amw, [('out_file', 'inputnode.in_file')]),\n        (skull_stripping, amw, [('outputnode.out_mask', 'inputnode.in_mask')]),\n        (hmsk, amw, [('outputnode.out_file', 'inputnode.head_mask')]),\n        (to_ras, iqmswf, [('out_file', 'inputnode.in_ras')]),\n        (skull_stripping, iqmswf, [('outputnode.out_corrected', 'inputnode.inu_corrected'),\n                                   (ss_bias_field, 'inputnode.in_inu'),\n                                   ('outputnode.out_mask', 'inputnode.brainmask')]),\n        (amw, iqmswf, [('outputnode.air_mask', 'inputnode.airmask'),\n                       ('outputnode.hat_mask', 'inputnode.hatmask'),\n                       ('outputnode.art_mask', 'inputnode.artmask'),\n                       ('outputnode.rot_mask', 'inputnode.rotmask')]),\n        (hmsk, bts, [('outputnode.out_denoised', 'inputnode.in_file')]),\n        (bts, iqmswf, [('outputnode.out_segm', 'inputnode.segmentation'),\n                       ('outputnode.out_pvms', 'inputnode.pvms')]),\n        (hmsk, iqmswf, [('outputnode.out_file', 'inputnode.headmask')]),\n        (to_ras, anat_report_wf, [('out_file', 'inputnode.in_ras')]),\n        (skull_stripping, anat_report_wf, [\n            ('outputnode.out_corrected', 'inputnode.inu_corrected'),\n            ('outputnode.out_mask', 'inputnode.brainmask')]),\n        (hmsk, anat_report_wf, [('outputnode.out_file', 'inputnode.headmask')]),\n        (amw, anat_report_wf, [\n            ('outputnode.air_mask', 'inputnode.airmask'),\n            ('outputnode.art_mask', 'inputnode.artmask'),\n            ('outputnode.rot_mask', 'inputnode.rotmask'),\n        ]),\n        (bts, anat_report_wf, [('outputnode.out_segm', 'inputnode.segmentation')]),\n        (iqmswf, anat_report_wf, [('outputnode.noisefit', 'inputnode.noisefit')]),\n        (iqmswf, anat_report_wf, [('outputnode.out_file', 'inputnode.in_iqms')]),\n        (iqmswf, outputnode, [('outputnode.out_file', 'out_json')]),\n    ])\n    # fmt: on\n\n    # Upload metrics\n    if not config.execution.no_sub:\n        from mriqc.interfaces.webapi import UploadIQMs\n\n        upldwf = pe.Node(\n            UploadIQMs(\n                endpoint=config.execution.webapi_url,\n                auth_token=config.execution.webapi_token,\n                strict=config.execution.upload_strict,\n            ),\n            name='UploadMetrics',\n        )\n\n        # fmt: off\n        workflow.connect([\n            (iqmswf, upldwf, [('outputnode.out_file', 'in_iqms')]),\n            (upldwf, anat_report_wf, [('api_id', 'inputnode.api_id')]),\n        ])\n        # fmt: on\n\n    return workflow\n\n\ndef spatial_normalization(name='SpatialNormalization'):\n    \"\"\"Create a simplified workflow to perform fast spatial normalization.\"\"\"\n    from niworkflows.interfaces.reportlets.registration import (\n        SpatialNormalizationRPT as RobustMNINormalization,\n    )\n\n    # Have the template id handy\n    tpl_id = config.workflow.template_id\n\n    # Define workflow interface\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(fields=['moving_image', 'moving_mask', 'modality']),\n        name='inputnode',\n    )\n    outputnode = pe.Node(\n        niu.IdentityInterface(fields=['out_tpms', 'out_report', 'ind2std_xfm']),\n        name='outputnode',\n    )\n\n    # Spatial normalization\n    norm = pe.Node(\n        RobustMNINormalization(\n            flavor=['testing', 'fast'][config.execution.debug],\n            num_threads=config.nipype.omp_nthreads,\n            float=config.execution.ants_float,\n            template=tpl_id,\n            generate_report=True,\n        ),\n        name='SpatialNormalization',\n        # Request all MultiProc processes when ants_nthreads > n_procs\n        num_threads=config.nipype.omp_nthreads,\n        mem_gb=3,\n    )\n    if config.workflow.species.lower() == 'human':\n        norm.inputs.reference_mask = str(\n            get_template(tpl_id, resolution=2, desc='brain', suffix='mask')\n        )\n    else:\n        norm.inputs.reference_image = str(get_template(tpl_id, suffix='T2w'))\n        norm.inputs.reference_mask = str(get_template(tpl_id, desc='brain', suffix='mask')[0])\n\n    # Project standard TPMs into T1w space\n    tpms_std2t1w = pe.MapNode(\n        ApplyTransforms(\n            dimension=3,\n            default_value=0,\n            interpolation='Gaussian',\n            float=config.execution.ants_float,\n        ),\n        iterfield=['input_image'],\n        name='tpms_std2t1w',\n    )\n    tpms_std2t1w.inputs.input_image = [\n        str(p)\n        for p in get_template(\n            config.workflow.template_id,\n            suffix='probseg',\n            resolution=(1 if config.workflow.species.lower() == 'human' else None),\n            label=['CSF', 'GM', 'WM'],\n        )\n    ]\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, norm, [('moving_image', 'moving_image'),\n                           ('moving_mask', 'moving_mask'),\n                           ('modality', 'reference')]),\n        (inputnode, tpms_std2t1w, [('moving_image', 'reference_image')]),\n        (norm, tpms_std2t1w, [\n            ('inverse_composite_transform', 'transforms'),\n        ]),\n        (norm, outputnode, [\n            ('composite_transform', 'ind2std_xfm'),\n            ('out_report', 'out_report'),\n        ]),\n        (tpms_std2t1w, outputnode, [('output_image', 'out_tpms')]),\n    ])\n    # fmt: on\n\n    return workflow\n\n\ndef init_brain_tissue_segmentation(name='brain_tissue_segmentation'):\n    \"\"\"\n    Setup a workflow for brain tissue segmentation.\n\n    .. workflow::\n\n        from mriqc.workflows.anatomical.base import init_brain_tissue_segmentation\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = init_brain_tissue_segmentation()\n\n    \"\"\"\n    from nipype.interfaces.ants import Atropos\n\n    def _format_tpm_names(in_files, fname_string=None):\n        import glob\n        from pathlib import Path\n\n        import nibabel as nb\n\n        out_path = Path.cwd().absolute()\n\n        # copy files to cwd and rename iteratively\n        for count, fname in enumerate(in_files):\n            img = nb.load(fname)\n            extension = ''.join(Path(fname).suffixes)\n            out_fname = f'priors_{1 + count:02}{extension}'\n            nb.save(img, Path(out_path, out_fname))\n\n        if fname_string is None:\n            fname_string = f'priors_%02d{extension}'\n\n        out_files = [str(prior) for prior in glob.glob(str(Path(out_path, f'priors*{extension}')))]\n\n        # return path with c-style format string for Atropos\n        file_format = str(Path(out_path, fname_string))\n        return file_format, out_files\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(fields=['in_file', 'brainmask', 'std_tpms']),\n        name='inputnode',\n    )\n    outputnode = pe.Node(\n        niu.IdentityInterface(fields=['out_segm', 'out_pvms']),\n        name='outputnode',\n    )\n\n    format_tpm_names = pe.Node(\n        niu.Function(\n            input_names=['in_files'],\n            output_names=['file_format'],\n            function=_format_tpm_names,\n            execution={'keep_inputs': True, 'remove_unnecessary_outputs': False},\n        ),\n        name='format_tpm_names',\n    )\n\n    segment = pe.Node(\n        Atropos(\n            initialization='PriorProbabilityImages',\n            number_of_tissue_classes=3,\n            prior_weighting=0.1,\n            mrf_radius=[1, 1, 1],\n            mrf_smoothing_factor=0.01,\n            save_posteriors=True,\n            out_classified_image_name='segment.nii.gz',\n            output_posteriors_name_template='segment_%02d.nii.gz',\n            num_threads=config.nipype.omp_nthreads,\n        ),\n        name='segmentation',\n        mem_gb=5,\n        num_threads=config.nipype.omp_nthreads,\n    )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, segment, [('in_file', 'intensity_images'),\n                              ('brainmask', 'mask_image')]),\n        (inputnode, format_tpm_names, [('std_tpms', 'in_files')]),\n        (format_tpm_names, segment, [(('file_format', _pop), 'prior_image')]),\n        (segment, outputnode, [('classified_image', 'out_segm'),\n                               ('posteriors', 'out_pvms')]),\n    ])\n    # fmt: on\n    return workflow\n\n\ndef compute_iqms(name='ComputeIQMs'):\n    \"\"\"\n    Setup the workflow that actually computes the IQMs.\n\n    .. workflow::\n\n        from mriqc.workflows.anatomical.base import compute_iqms\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = compute_iqms()\n\n    \"\"\"\n\n    from mriqc.interfaces.anatomical import Harmonize\n    from mriqc.workflows.utils import _tofloat\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'in_file',\n                'metadata',\n                'entities',\n                'in_ras',\n                'brainmask',\n                'airmask',\n                'artmask',\n                'headmask',\n                'rotmask',\n                'hatmask',\n                'segmentation',\n                'inu_corrected',\n                'in_inu',\n                'pvms',\n                'std_tpms',\n            ]\n        ),\n        name='inputnode',\n    )\n    outputnode = pe.Node(\n        niu.IdentityInterface(fields=['out_file', 'noisefit']),\n        name='outputnode',\n    )\n\n    # Add provenance\n    addprov = pe.Node(AddProvenance(), name='provenance', run_without_submitting=True)\n\n    # AFNI check smoothing\n    fwhm_interface = get_fwhmx()\n\n    fwhm = pe.Node(fwhm_interface, name='smoothness')\n\n    # Harmonize\n    homog = pe.Node(Harmonize(), name='harmonize')\n    if config.workflow.species.lower() != 'human':\n        homog.inputs.erodemsk = False\n        homog.inputs.thresh = 0.8\n\n    # Mortamet's QI2\n    getqi2 = pe.Node(ComputeQI2(), name='ComputeQI2')\n\n    # Compute python-coded measures\n    measures = pe.Node(StructuralQC(human=config.workflow.species.lower() == 'human'), 'measures')\n\n    datasink = pe.Node(\n        IQMFileSink(\n            out_dir=config.execution.output_dir,\n            dataset=config.execution.dsname,\n        ),\n        name='datasink',\n        run_without_submitting=True,\n    )\n\n    def _getwm(inlist):\n        return inlist[-1]\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, datasink, [\n            ('in_file', 'in_file'),\n            (('in_file', _get_mod), 'modality'),\n            ('metadata', 'metadata'),\n            ('entities', 'entities')]),\n        (inputnode, addprov, [(('in_file', _get_mod), 'modality')]),\n        (inputnode, addprov, [('in_file', 'in_file'),\n                              ('airmask', 'air_msk'),\n                              ('rotmask', 'rot_msk')]),\n        (inputnode, getqi2, [('in_ras', 'in_file'),\n                             ('hatmask', 'air_msk')]),\n        (inputnode, homog, [('inu_corrected', 'in_file'),\n                            ('brainmask', 'brain_mask'),\n                            (('pvms', _getwm), 'wm_mask')]),\n        (inputnode, measures, [('in_inu', 'in_bias'),\n                               ('in_ras', 'in_file'),\n                               ('airmask', 'air_msk'),\n                               ('headmask', 'head_msk'),\n                               ('artmask', 'artifact_msk'),\n                               ('rotmask', 'rot_msk'),\n                               ('segmentation', 'in_segm'),\n                               ('pvms', 'in_pvms'),\n                               ('std_tpms', 'mni_tpms')]),\n        (inputnode, fwhm, [('in_ras', 'in_file'),\n                           ('brainmask', 'mask')]),\n        (homog, measures, [('out_file', 'in_noinu')]),\n        (fwhm, measures, [(('fwhm', _tofloat), 'in_fwhm')]),\n        (measures, datasink, [('out_qc', 'root')]),\n        (addprov, datasink, [('out_prov', 'provenance')]),\n        (getqi2, datasink, [('qi2', 'qi_2')]),\n        (getqi2, outputnode, [('out_file', 'noisefit')]),\n        (datasink, outputnode, [('out_file', 'out_file')]),\n    ])\n    # fmt: on\n\n    return workflow\n\n\ndef headmsk_wf(name='HeadMaskWorkflow', omp_nthreads=1):\n    \"\"\"\n    Computes a head mask as in [Mortamet2009]_.\n\n    .. workflow::\n\n        from mriqc.testing import mock_config\n        from mriqc.workflows.anatomical.base import headmsk_wf\n        with mock_config():\n            wf = headmsk_wf()\n\n    \"\"\"\n\n    from niworkflows.interfaces.nibabel import ApplyMask\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(fields=['in_file', 'brainmask', 'in_tpms']), name='inputnode'\n    )\n    outputnode = pe.Node(\n        niu.IdentityInterface(fields=['out_file', 'out_denoised']), name='outputnode'\n    )\n\n    def _select_wm(inlist):\n        return [f for f in inlist if 'WM' in f][0]\n\n    enhance = pe.Node(\n        niu.Function(\n            input_names=['in_file', 'wm_tpm'],\n            output_names=['out_file'],\n            function=_enhance,\n        ),\n        name='Enhance',\n        num_threads=omp_nthreads,\n    )\n\n    gradient = pe.Node(\n        niu.Function(\n            input_names=['in_file', 'brainmask', 'sigma'],\n            output_names=['out_file'],\n            function=image_gradient,\n        ),\n        name='Grad',\n        num_threads=omp_nthreads,\n    )\n    thresh = pe.Node(\n        niu.Function(\n            input_names=['in_file', 'brainmask', 'aniso', 'thresh'],\n            output_names=['out_file'],\n            function=gradient_threshold,\n        ),\n        name='GradientThreshold',\n        num_threads=omp_nthreads,\n    )\n    if config.workflow.species != 'human':\n        gradient.inputs.sigma = 3.0\n        thresh.inputs.aniso = True\n        thresh.inputs.thresh = 4.0\n\n    apply_mask = pe.Node(ApplyMask(), name='apply_mask')\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, enhance, [('in_file', 'in_file'),\n                              (('in_tpms', _select_wm), 'wm_tpm')]),\n        (inputnode, thresh, [('brainmask', 'brainmask')]),\n        (inputnode, gradient, [('brainmask', 'brainmask')]),\n        (inputnode, apply_mask, [('brainmask', 'in_mask')]),\n        (enhance, gradient, [('out_file', 'in_file')]),\n        (gradient, thresh, [('out_file', 'in_file')]),\n        (enhance, apply_mask, [('out_file', 'in_file')]),\n        (thresh, outputnode, [('out_file', 'out_file')]),\n        (apply_mask, outputnode, [('out_file', 'out_denoised')]),\n    ])\n    # fmt: on\n\n    return workflow\n\n\ndef airmsk_wf(name='AirMaskWorkflow'):\n    \"\"\"\n    Calculate air, artifacts and \"hat\" masks to evaluate noise in the background.\n\n    This workflow mostly addresses the implementation of Step 1 in [Mortamet2009]_.\n    This work proposes to look at the signal distribution in the background, where\n    no signals are expected, to evaluate the spread of the noise.\n    It is in the background where [Mortamet2009]_ proposed to also look at the presence\n    of ghosts and artifacts, where they are very easy to isolate.\n\n    However, [Mortamet2009]_ proposes not to look at the background around the face\n    because of the likely signal leakage through the phase-encoding axis sourcing from\n    eyeballs (and their motion).\n    To avoid that, [Mortamet2009]_ proposed atlas-based identification of two landmarks\n    (nasion and cerebellar projection on to the occipital bone).\n    MRIQC, for simplicity, used a such a mask created in MNI152NLin2009cAsym space and\n    projected it on to the individual.\n    Such a solution is inadequate because it doesn't drop full in-plane slices as there\n    will be a large rotation of the individual's tilt of the head with respect to the\n    template.\n    The new implementation (23.1.x series) follows [Mortamet2009]_ more closely,\n    projecting the two landmarks from the template space and leveraging\n    *NiTransforms* to do that.\n\n    .. workflow::\n\n        from mriqc.testing import mock_config\n        from mriqc.workflows.anatomical.base import airmsk_wf\n        with mock_config():\n            wf = airmsk_wf()\n\n    \"\"\"\n    workflow = pe.Workflow(name=name)\n\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'in_file',\n                'in_mask',\n                'head_mask',\n                'ind2std_xfm',\n            ]\n        ),\n        name='inputnode',\n    )\n    outputnode = pe.Node(\n        niu.IdentityInterface(fields=['hat_mask', 'air_mask', 'art_mask', 'rot_mask']),\n        name='outputnode',\n    )\n\n    rotmsk = pe.Node(RotationMask(), name='RotationMask')\n    qi1 = pe.Node(ArtifactMask(), name='ArtifactMask')\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, rotmsk, [('in_file', 'in_file')]),\n        (inputnode, qi1, [('in_file', 'in_file'),\n                          ('head_mask', 'head_mask'),\n                          ('ind2std_xfm', 'ind2std_xfm')]),\n        (qi1, outputnode, [('out_hat_msk', 'hat_mask'),\n                           ('out_air_msk', 'air_mask'),\n                           ('out_art_msk', 'art_mask')]),\n        (rotmsk, outputnode, [('out_file', 'rot_mask')])\n    ])\n    # fmt: on\n\n    return workflow\n\n\ndef _binarize(in_file, threshold=0.5, out_file=None):\n    import os.path as op\n\n    import nibabel as nb\n    import numpy as np\n\n    if out_file is None:\n        fname, ext = op.splitext(op.basename(in_file))\n        if ext == '.gz':\n            fname, ext2 = op.splitext(fname)\n            ext = ext2 + ext\n        out_file = op.abspath(f'{fname}_bin{ext}')\n\n    nii = nb.load(in_file)\n    data = nii.get_fdata() > threshold\n\n    hdr = nii.header.copy()\n    hdr.set_data_dtype(np.uint8)\n    nb.Nifti1Image(data.astype(np.uint8), nii.affine, hdr).to_filename(out_file)\n    return out_file\n\n\ndef _enhance(in_file, wm_tpm, out_file=None):\n    import nibabel as nb\n    import numpy as np\n\n    from mriqc.workflows.utils import generate_filename\n\n    imnii = nb.load(in_file)\n    data = imnii.get_fdata(dtype=np.float32)\n    range_max = np.percentile(data[data > 0], 99.98)\n    excess = data > range_max\n\n    wm_prob = nb.load(wm_tpm).get_fdata()\n    wm_prob[wm_prob < 0] = 0  # Ensure no negative values\n    wm_prob[excess] = 0  # Ensure no outliers are considered\n\n    # Calculate weighted mean and standard deviation\n    wm_mu = np.average(data, weights=wm_prob)\n    wm_sigma = np.sqrt(np.average((data - wm_mu) ** 2, weights=wm_prob))\n\n    # Resample signal excess pixels\n    data[excess] = np.random.normal(loc=wm_mu, scale=wm_sigma, size=excess.sum())\n\n    out_file = out_file or str(generate_filename(in_file, suffix='enhanced').absolute())\n    nb.Nifti1Image(data, imnii.affine, imnii.header).to_filename(out_file)\n    return out_file\n\n\ndef image_gradient(in_file, brainmask, sigma=4.0, out_file=None):\n    \"\"\"Computes the magnitude gradient of an image using numpy\"\"\"\n    import nibabel as nb\n    import numpy as np\n    from scipy.ndimage import gaussian_gradient_magnitude as gradient\n\n    from mriqc.workflows.utils import generate_filename\n\n    imnii = nb.load(in_file)\n    mask = np.bool_(nb.load(brainmask).dataobj)\n    data = imnii.get_fdata(dtype=np.float32)\n    datamax = np.percentile(data.reshape(-1), 99.5)\n    data *= 100 / datamax\n    data[mask] = 100\n\n    zooms = np.array(imnii.header.get_zooms()[:3])\n    sigma_xyz = 2 - zooms / min(zooms)\n    grad = gradient(data, sigma * sigma_xyz)\n    gradmax = np.percentile(grad.reshape(-1), 99.5)\n    grad *= 100.0\n    grad /= gradmax\n    grad[mask] = 100\n\n    out_file = out_file or str(generate_filename(in_file, suffix='grad').absolute())\n    nb.Nifti1Image(grad, imnii.affine, imnii.header).to_filename(out_file)\n    return out_file\n\n\ndef gradient_threshold(in_file, brainmask, thresh=15.0, out_file=None, aniso=False):\n    \"\"\"Compute a threshold from the histogram of the magnitude gradient image\"\"\"\n    import nibabel as nb\n    import numpy as np\n    from scipy import ndimage as sim\n\n    from mriqc.workflows.utils import generate_filename\n\n    if not aniso:\n        struct = sim.iterate_structure(sim.generate_binary_structure(3, 2), 2)\n    else:\n        # Generate an anisotropic binary structure, taking into account slice thickness\n        img = nb.load(in_file)\n        zooms = img.header.get_zooms()\n        dist = max(zooms)\n        dim = img.header['dim'][0]\n\n        x = np.ones((5) * np.ones(dim, dtype=np.int8))\n        np.put(x, x.size // 2, 0)\n        dist_matrix = np.round(sim.distance_transform_edt(x, sampling=zooms), 5)\n        struct = dist_matrix <= dist\n\n    imnii = nb.load(in_file)\n\n    hdr = imnii.header.copy()\n    hdr.set_data_dtype(np.uint8)\n\n    data = imnii.get_fdata(dtype=np.float32)\n\n    mask = np.zeros_like(data, dtype=np.uint8)\n    mask[data > thresh] = 1\n    mask = sim.binary_closing(mask, struct, iterations=2).astype(np.uint8)\n    mask = sim.binary_erosion(mask, sim.generate_binary_structure(3, 2)).astype(np.uint8)\n\n    segdata = np.asanyarray(nb.load(brainmask).dataobj) > 0\n    segdata = sim.binary_dilation(segdata, struct, iterations=2, border_value=1).astype(np.uint8)\n    mask[segdata] = 1\n\n    # Remove small objects\n    label_im, nb_labels = sim.label(mask)\n    artmsk = np.zeros_like(mask)\n    if nb_labels > 2:\n        sizes = sim.sum(mask, label_im, list(range(nb_labels + 1)))\n        ordered = sorted(zip(sizes, list(range(nb_labels + 1))), reverse=True)\n        for _, label in ordered[2:]:\n            mask[label_im == label] = 0\n            artmsk[label_im == label] = 1\n\n    mask = sim.binary_fill_holes(mask, struct).astype(np.uint8)  # pylint: disable=no-member\n\n    out_file = out_file or str(generate_filename(in_file, suffix='gradmask').absolute())\n    nb.Nifti1Image(mask, imnii.affine, hdr).to_filename(out_file)\n    return out_file\n\n\ndef _get_imgtype(in_file):\n    from mriqc.workflows.anatomical.base import _get_mod\n\n    return int(_get_mod(in_file)[1])\n\n\ndef _get_mod(in_file):\n    from pathlib import Path\n\n    in_file = Path(in_file)\n    extension = ''.join(in_file.suffixes)\n    return in_file.name.replace(extension, '').split('_')[-1]\n\n\ndef _pop(inlist):\n    if isinstance(inlist, (list, tuple)):\n        return inlist[0]\n    return inlist\n"
  },
  {
    "path": "mriqc/workflows/anatomical/output.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2023 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Writing out anatomical reportlets.\"\"\"\n\nfrom nipype.interfaces import utility as niu\nfrom nipype.pipeline import engine as pe\n\nfrom mriqc import config\nfrom mriqc.interfaces import DerivativesDataSink\n\n\ndef init_anat_report_wf(name: str = 'anat_report_wf'):\n    \"\"\"\n    Generate the components of the individual report.\n\n    .. workflow::\n\n        from mriqc.workflows.anatomical.output import init_anat_report_wf\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = init_anat_report_wf()\n\n    \"\"\"\n    from nireports.interfaces import PlotMosaic\n\n    # from mriqc.interfaces.reports import IndividualReport\n\n    verbose = config.execution.verbose_reports\n    reportlets_dir = config.execution.work_dir / 'reportlets'\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'in_ras',\n                'brainmask',\n                'headmask',\n                'airmask',\n                'artmask',\n                'rotmask',\n                'segmentation',\n                'inu_corrected',\n                'noisefit',\n                'in_iqms',\n                'mni_report',\n                'api_id',\n                'name_source',\n            ]\n        ),\n        name='inputnode',\n    )\n\n    mosaic_zoom = pe.Node(\n        PlotMosaic(cmap='Greys_r'),\n        name='PlotMosaicZoomed',\n    )\n\n    mosaic_noise = pe.Node(\n        PlotMosaic(only_noise=True, cmap='viridis_r'),\n        name='PlotMosaicNoise',\n    )\n    if config.workflow.species.lower() in ('rat', 'mouse'):\n        mosaic_zoom.inputs.view = ['coronal', 'axial']\n        mosaic_noise.inputs.view = ['coronal', 'axial']\n\n    ds_report_zoomed = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='zoomed',\n            datatype='figures',\n        ),\n        name='ds_report_zoomed',\n        run_without_submitting=True,\n    )\n\n    ds_report_background = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='background',\n            datatype='figures',\n        ),\n        name='ds_report_background',\n        run_without_submitting=True,\n    )\n\n    # fmt: off\n    workflow.connect([\n        # (inputnode, rnode, [(\"in_iqms\", \"in_iqms\")]),\n        (inputnode, mosaic_zoom, [('in_ras', 'in_file'),\n                                  ('brainmask', 'bbox_mask_file')]),\n        (inputnode, mosaic_noise, [('in_ras', 'in_file')]),\n        (inputnode, ds_report_zoomed, [('name_source', 'source_file')]),\n        (inputnode, ds_report_background, [('name_source', 'source_file')]),\n        (mosaic_zoom, ds_report_zoomed, [('out_file', 'in_file')]),\n        (mosaic_noise, ds_report_background, [('out_file', 'in_file')]),\n    ])\n    # fmt: on\n\n    if not verbose:\n        return workflow\n\n    from nireports.interfaces import PlotContours\n\n    display_mode = 'y' if config.workflow.species.lower() in ('rat', 'mouse') else 'z'\n    plot_segm = pe.Node(\n        PlotContours(\n            display_mode=display_mode,\n            levels=[0.5, 1.5, 2.5],\n            cut_coords=10,\n            colors=['r', 'g', 'b'],\n        ),\n        name='PlotSegmentation',\n    )\n\n    ds_report_segm = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='segmentation',\n            datatype='figures',\n        ),\n        name='ds_report_segm',\n        run_without_submitting=True,\n    )\n\n    plot_bmask = pe.Node(\n        PlotContours(\n            display_mode=display_mode,\n            levels=[0.5],\n            colors=['r'],\n            cut_coords=10,\n            out_file='bmask',\n        ),\n        name='PlotBrainmask',\n    )\n\n    ds_report_bmask = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='brainmask',\n            datatype='figures',\n        ),\n        name='ds_report_bmask',\n        run_without_submitting=True,\n    )\n\n    plot_artmask = pe.Node(\n        PlotContours(\n            display_mode=display_mode,\n            levels=[0.5],\n            colors=['r'],\n            cut_coords=10,\n            out_file='artmask',\n            saturate=True,\n        ),\n        name='PlotArtmask',\n    )\n\n    ds_report_artmask = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='artifacts',\n            datatype='figures',\n        ),\n        name='ds_report_artmask',\n        run_without_submitting=True,\n    )\n\n    # NOTE: humans switch on these two to coronal view.\n    display_mode = 'y' if config.workflow.species.lower() in ('rat', 'mouse') else 'x'\n    plot_airmask = pe.Node(\n        PlotContours(\n            display_mode=display_mode,\n            levels=[0.5],\n            colors=['r'],\n            cut_coords=6,\n            out_file='airmask',\n        ),\n        name='PlotAirmask',\n    )\n\n    ds_report_airmask = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='airmask',\n            datatype='figures',\n        ),\n        name='ds_report_airmask',\n        run_without_submitting=True,\n    )\n\n    plot_headmask = pe.Node(\n        PlotContours(\n            display_mode=display_mode,\n            levels=[0.5],\n            colors=['r'],\n            cut_coords=6,\n            out_file='headmask',\n        ),\n        name='PlotHeadmask',\n    )\n\n    ds_report_headmask = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='head',\n            datatype='figures',\n        ),\n        name='ds_report_headmask',\n        run_without_submitting=True,\n    )\n\n    ds_report_norm = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='norm',\n            datatype='figures',\n        ),\n        name='ds_report_norm',\n        run_without_submitting=True,\n    )\n\n    ds_report_noisefit = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='noisefit',\n            datatype='figures',\n        ),\n        name='ds_report_noisefit',\n        run_without_submitting=True,\n    )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, ds_report_segm, [('name_source', 'source_file')]),\n        (inputnode, ds_report_bmask, [('name_source', 'source_file')]),\n        (inputnode, ds_report_artmask, [('name_source', 'source_file')]),\n        (inputnode, ds_report_airmask, [('name_source', 'source_file')]),\n        (inputnode, ds_report_headmask, [('name_source', 'source_file')]),\n        (inputnode, ds_report_norm, [('mni_report', 'in_file'),\n                                     ('name_source', 'source_file')]),\n        (inputnode, ds_report_noisefit, [('noisefit', 'in_file'),\n                                         ('name_source', 'source_file')]),\n        (inputnode, plot_segm, [('in_ras', 'in_file'),\n                                ('segmentation', 'in_contours')]),\n        (inputnode, plot_bmask, [('in_ras', 'in_file'),\n                                 ('brainmask', 'in_contours')]),\n        (inputnode, plot_headmask, [('in_ras', 'in_file'),\n                                    ('headmask', 'in_contours')]),\n        (inputnode, plot_airmask, [('in_ras', 'in_file'),\n                                   ('airmask', 'in_contours')]),\n        (inputnode, plot_artmask, [('in_ras', 'in_file'),\n                                   ('artmask', 'in_contours')]),\n        (plot_bmask, ds_report_bmask, [('out_file', 'in_file')]),\n        (plot_segm, ds_report_segm, [('out_file', 'in_file')]),\n        (plot_artmask, ds_report_artmask, [('out_file', 'in_file')]),\n        (plot_headmask, ds_report_headmask, [('out_file', 'in_file')]),\n        (plot_airmask, ds_report_airmask, [('out_file', 'in_file')]),\n    ])\n    # fmt: on\n\n    return workflow\n"
  },
  {
    "path": "mriqc/workflows/core.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nCombines the structural and functional MRI workflows.\n\"\"\"\n\nfrom nipype.pipeline.engine import Workflow\n\nfrom mriqc.workflows.anatomical.base import anat_qc_workflow\nfrom mriqc.workflows.diffusion.base import dmri_qc_workflow\nfrom mriqc.workflows.functional.base import fmri_qc_workflow\n\nANATOMICAL_KEYS = 't1w', 't2w'\nFMRI_KEY = 'bold'\nDMRI_KEY = 'dwi'\n\n\ndef init_mriqc_wf():\n    \"\"\"Create a multi-subject MRIQC workflow.\"\"\"\n    from mriqc import config\n\n    # Create parent workflow\n    workflow = Workflow(name='mriqc_wf')\n    workflow.base_dir = config.execution.work_dir\n\n    # Create fMRI QC workflow\n    if FMRI_KEY in config.workflow.inputs:\n        workflow.add_nodes([fmri_qc_workflow()])\n\n    # Create dMRI QC workflow\n    if DMRI_KEY in config.workflow.inputs:\n        workflow.add_nodes([dmri_qc_workflow()])\n\n    # Create sMRI QC workflow\n    input_keys = config.workflow.inputs.keys()\n    if any(key in input_keys for key in ANATOMICAL_KEYS):\n        workflow.add_nodes([anat_qc_workflow()])\n\n    # Return non-empty workflow, else None\n    if workflow._get_all_nodes():\n        return workflow\n"
  },
  {
    "path": "mriqc/workflows/diffusion/__init__.py",
    "content": ""
  },
  {
    "path": "mriqc/workflows/diffusion/base.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2023 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nDiffusion MRI workflow\n======================\n\n.. image :: _static/diffusion_workflow_source.svg\n\nThe diffusion workflow follows the following steps:\n\n#. Sanitize (revise data types and xforms) input data, read\n   associated metadata and discard non-steady state frames.\n#. :abbr:`HMC (head-motion correction)` based on ``3dvolreg`` from\n   AFNI -- :py:func:`hmc`.\n#. Skull-stripping of the time-series (AFNI) --\n   :py:func:`dmri_bmsk_workflow`.\n#. Calculate mean time-series, and :abbr:`tSNR (temporal SNR)`.\n#. Spatial Normalization to MNI (ANTs) -- :py:func:`epi_mni_align`\n#. Extraction of IQMs -- :py:func:`compute_iqms`.\n#. Individual-reports generation --\n   :py:func:`~mriqc.workflows.diffusion.output.init_dwi_report_wf`.\n\nThis workflow is orchestrated by :py:func:`dmri_qc_workflow`.\n\"\"\"\n\nfrom nipype.interfaces import utility as niu\nfrom nipype.pipeline import engine as pe\n\nfrom mriqc import config\nfrom mriqc.workflows.diffusion.output import init_dwi_report_wf\n\nDEFAULT_MEMORY_MIN_GB = 0.01\n\n\ndef dmri_qc_workflow(name='dwiMRIQC'):\n    \"\"\"\n    Initialize the dMRI-QC workflow.\n\n    .. workflow::\n\n        import os.path as op\n        from mriqc.workflows.diffusion.base import dmri_qc_workflow\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = dmri_qc_workflow()\n\n    \"\"\"\n    from nipype.interfaces.afni import Volreg\n    from nipype.interfaces.mrtrix3.preprocess import DWIDenoise\n    from niworkflows.interfaces.header import SanitizeImage\n    from niworkflows.interfaces.images import RobustAverage\n\n    from mriqc.interfaces.diffusion import (\n        PIESNO,\n        CCSegmentation,\n        CorrectSignalDrift,\n        DiffusionModel,\n        ExtractOrientations,\n        NumberOfShells,\n        ReadDWIMetadata,\n        SpikingVoxelsMask,\n        WeightedStat,\n    )\n    from mriqc.messages import BUILDING_WORKFLOW\n    from mriqc.workflows.shared import synthstrip_wf as dmri_bmsk_workflow\n\n    # Enable if necessary\n    # mem_gb = config.workflow.biggest_file_gb['dwi']\n    dataset = config.workflow.inputs['dwi']\n    metadata = config.workflow.inputs_metadata['dwi']\n    entities = config.workflow.inputs_entities['dwi']\n    message = BUILDING_WORKFLOW.format(\n        modality='diffusion',\n        detail=f'for {len(dataset)} NIfTI files.',\n    )\n    config.loggers.workflow.info(message)\n\n    # Define workflow, inputs and outputs\n    # 0. Get data, put it in RAS orientation\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=['in_file', 'metadata', 'entities'],\n        ),\n        name='inputnode',\n    )\n    inputnode.synchronize = True  # Do not test combinations of iterables\n    inputnode.iterables = [\n        ('in_file', dataset),\n        ('metadata', metadata),\n        ('entities', entities),\n    ]\n\n    sanitize = pe.Node(\n        SanitizeImage(\n            n_volumes_to_discard=0,\n            max_32bit=config.execution.float32,\n        ),\n        name='sanitize',\n        mem_gb=4.0,\n    )\n\n    # Workflow --------------------------------------------------------\n\n    # Read metadata & bvec/bval, estimate number of shells, extract and split B0s\n    load_bmat = pe.Node(\n        ReadDWIMetadata(index_db=config.execution.bids_database_dir),\n        name='load_bmat',\n    )\n    shells = pe.Node(NumberOfShells(), name='shells')\n    get_lowb = pe.Node(\n        ExtractOrientations(),\n        name='get_lowb',\n        n_procs=max(1, config.nipype.nprocs // 2),\n    )\n\n    # Generate B0 reference\n    dwi_ref = pe.Node(\n        RobustAverage(mc_method=None),\n        name='dwi_ref',\n        n_procs=max(1, config.nipype.nprocs // 2),\n    )\n\n    hmc_b0 = pe.Node(\n        Volreg(args='-Fourier -twopass', zpad=4, outputtype='NIFTI_GZ'),\n        name='hmc_b0',\n        mem_gb=3.0,\n        n_procs=config.nipype.nprocs,\n    )\n\n    # Calculate brainmask\n    dmri_bmsk = dmri_bmsk_workflow(omp_nthreads=config.nipype.omp_nthreads)\n\n    # HMC: head motion correct\n    hmcwf = hmc_workflow()\n\n    get_hmc_shells = pe.MapNode(\n        ExtractOrientations(),\n        name='get_hmc_shells',\n        iterfield=['indices'],\n    )\n\n    # Split shells and compute some stats\n    averages = pe.MapNode(\n        WeightedStat(),\n        name='averages',\n        n_procs=max(1, config.nipype.nprocs // 2),\n        iterfield=['in_weights'],\n    )\n    stddev = pe.MapNode(\n        WeightedStat(stat='std'),\n        name='stddev',\n        n_procs=max(1, config.nipype.nprocs // 2),\n        iterfield=['in_weights'],\n    )\n\n    dwidenoise = pe.Node(\n        DWIDenoise(\n            noise='noisemap.nii.gz',\n            nthreads=config.nipype.omp_nthreads,\n        ),\n        name='dwidenoise',\n        n_procs=config.nipype.nprocs,\n    )\n    drift = pe.Node(\n        CorrectSignalDrift(),\n        name='drift',\n        n_procs=max(1, config.nipype.nprocs // 2),\n    )\n    sp_mask = pe.Node(\n        SpikingVoxelsMask(),\n        name='sp_mask',\n        n_procs=max(1, config.nipype.nprocs // 2),\n    )\n\n    # Fit DTI/DKI model\n    dwimodel = pe.Node(\n        DiffusionModel(),\n        name='dwimodel',\n        n_procs=config.nipype.nprocs,\n    )\n\n    # Calculate CC mask\n    cc_mask = pe.Node(\n        CCSegmentation(),\n        name='cc_mask',\n        n_procs=max(1, config.nipype.nprocs // 2),\n    )\n\n    # Run PIESNO noise estimation\n    piesno = pe.Node(\n        PIESNO(),\n        name='piesno',\n        n_procs=max(1, config.nipype.nprocs // 2),\n    )\n\n    # EPI to MNI registration\n    spatial_norm = epi_mni_align()\n\n    # Compute IQMs\n    iqms_wf = compute_iqms()\n\n    # Generate outputs\n    dwi_report_wf = init_dwi_report_wf()\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, load_bmat, [('in_file', 'in_file')]),\n        (inputnode, dwi_report_wf, [\n            ('in_file', 'inputnode.name_source'),\n        ]),\n        (inputnode, iqms_wf, [\n            ('in_file', 'inputnode.in_file'),\n            ('metadata', 'inputnode.metadata'),\n            ('entities', 'inputnode.entities'),\n        ]),\n        (inputnode, sanitize, [('in_file', 'in_file')]),\n        (sanitize, dwi_ref, [('out_file', 'in_file')]),\n        (sanitize, sp_mask, [('out_file', 'in_file')]),\n        (sanitize, piesno, [('out_file', 'in_file')]),\n        (shells, dwi_ref, [(('b_masks', _first), 't_mask')]),\n        (shells, sp_mask, [('b_masks', 'b_masks')]),\n        (load_bmat, shells, [('out_bval_file', 'in_bvals')]),\n        (sanitize, drift, [('out_file', 'full_epi')]),\n        (shells, get_lowb, [(('b_indices', _first), 'indices')]),\n        (sanitize, get_lowb, [('out_file', 'in_file')]),\n        (load_bmat, drift, [('out_bval_file', 'bval_file')]),\n        (get_lowb, hmc_b0, [('out_file', 'in_file')]),\n        (dwi_ref, hmc_b0, [('out_file', 'basefile')]),\n        (hmc_b0, drift, [('out_file', 'in_file')]),\n        (shells, drift, [(('b_indices', _first), 'b0_ixs')]),\n        (dwi_ref, dmri_bmsk, [('out_file', 'inputnode.in_files')]),\n        (dmri_bmsk, sp_mask, [('outputnode.out_mask', 'brain_mask')]),\n        (dmri_bmsk, drift, [('outputnode.out_mask', 'brainmask_file')]),\n        (drift, hmcwf, [('out_full_file', 'inputnode.in_file')]),\n        (load_bmat, hmcwf, [('out_bvec_file', 'inputnode.in_bvec')]),\n        (drift, averages, [('out_full_file', 'in_file')]),\n        (drift, stddev, [('out_full_file', 'in_file')]),\n        (shells, averages, [('b_masks', 'in_weights')]),\n        (averages, hmcwf, [(('out_file', _first), 'inputnode.reference')]),\n        (shells, stddev, [('b_masks', 'in_weights')]),\n        (shells, dwimodel, [('out_data', 'bvals'),\n                            ('n_shells', 'n_shells')]),\n        (load_bmat, dwimodel, [('out_bvec_file', 'bvec_file')]),\n        (drift, dwidenoise, [('out_full_file', 'in_file')]),\n        (dmri_bmsk, dwidenoise, [('outputnode.out_mask', 'mask')]),\n        (dwidenoise, dwimodel, [('out_file', 'in_file')]),\n        (dmri_bmsk, dwimodel, [('outputnode.out_mask', 'brain_mask')]),\n        (load_bmat, get_hmc_shells, [('out_bvec_file', 'in_bvec_file')]),\n        (shells, get_hmc_shells, [('b_indices', 'indices')]),\n        (hmcwf, get_hmc_shells, [('outputnode.out_file', 'in_file')]),\n        (dwimodel, cc_mask, [('out_fa', 'in_fa'),\n                             ('out_cfa', 'in_cfa')]),\n        (load_bmat, iqms_wf, [\n            ('out_bval_file', 'inputnode.b_values_file'),\n            ('qspace_neighbors', 'inputnode.qspace_neighbors'),\n        ]),\n        (averages, iqms_wf, [(('out_file', _first), 'inputnode.in_b0')]),\n        (sp_mask, iqms_wf, [('out_mask', 'inputnode.spikes_mask')]),\n        (piesno, iqms_wf, [('sigma', 'inputnode.piesno_sigma')]),\n        (hmcwf, iqms_wf, [('outputnode.out_fd', 'inputnode.framewise_displacement'),\n                          ('outputnode.out_bvec', 'inputnode.in_bvec_rotated'),\n                          ('outputnode.out_bvec_diff', 'inputnode.in_bvec_diff')]),\n        (dwimodel, iqms_wf, [('out_fa', 'inputnode.in_fa'),\n                             ('out_cfa', 'inputnode.in_cfa'),\n                             ('out_fa_nans', 'inputnode.in_fa_nans'),\n                             ('out_fa_degenerate', 'inputnode.in_fa_degenerate'),\n                             ('out_md', 'inputnode.in_md')]),\n        (dmri_bmsk, iqms_wf, [('outputnode.out_mask', 'inputnode.brain_mask')]),\n        (cc_mask, iqms_wf, [('out_mask', 'inputnode.cc_mask'),\n                            ('wm_finalmask', 'inputnode.wm_mask')]),\n        (shells, iqms_wf, [('n_shells', 'inputnode.n_shells'),\n                           ('b_values', 'inputnode.b_values_shells')]),\n        (get_hmc_shells, iqms_wf, [('out_file', 'inputnode.in_shells'),\n                                   ('out_bvec', 'inputnode.in_bvec')]),\n        (dwidenoise, iqms_wf, [('noise', 'inputnode.in_noise')]),\n        (dwi_ref, spatial_norm, [('out_file', 'inputnode.epi_mean')]),\n        (dmri_bmsk, spatial_norm, [('outputnode.out_mask', 'inputnode.epi_mask')]),\n        (iqms_wf, dwi_report_wf, [('outputnode.noise_floor', 'inputnode.noise_floor')]),\n        (shells, dwi_report_wf, [('b_dict', 'inputnode.in_bdict')]),\n        (dmri_bmsk, dwi_report_wf, [('outputnode.out_mask', 'inputnode.brain_mask')]),\n        (shells, dwi_report_wf, [('b_values', 'inputnode.in_shells')]),\n        (averages, dwi_report_wf, [('out_file', 'inputnode.in_avgmap')]),\n        (stddev, dwi_report_wf, [('out_file', 'inputnode.in_stdmap')]),\n        (drift, dwi_report_wf, [('out_full_file', 'inputnode.in_epi')]),\n        (dwimodel, dwi_report_wf, [('out_fa', 'inputnode.in_fa'),\n                                   ('out_md', 'inputnode.in_md')]),\n        (spatial_norm, dwi_report_wf, [('outputnode.epi_parc', 'inputnode.in_parcellation')]),\n    ])\n    # fmt: on\n    return workflow\n\n\ndef compute_iqms(name='ComputeIQMs'):\n    \"\"\"\n    Initialize the workflow that actually computes the IQMs.\n\n    .. workflow::\n\n        from mriqc.workflows.diffusion.base import compute_iqms\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = compute_iqms()\n\n    \"\"\"\n\n    from mriqc.interfaces import IQMFileSink\n    from mriqc.interfaces.diffusion import DiffusionQC\n    from mriqc.interfaces.reports import AddProvenance\n\n    # from mriqc.workflows.utils import _tofloat, get_fwhmx\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'in_file',\n                'metadata',\n                'entities',\n                'in_shells',\n                'n_shells',\n                'b_values_file',\n                'b_values_shells',\n                'in_bvec',\n                'in_bvec_rotated',\n                'in_bvec_diff',\n                'in_b0',\n                'in_fa',\n                'in_cfa',\n                'in_fa_nans',\n                'in_fa_degenerate',\n                'in_md',\n                'in_noise',\n                'brain_mask',\n                'wm_mask',\n                'cc_mask',\n                'spikes_mask',\n                'framewise_displacement',\n                'qspace_neighbors',\n                'piesno_sigma',\n            ]\n        ),\n        name='inputnode',\n    )\n    outputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'out_file',\n                'noise_floor',\n            ]\n        ),\n        name='outputnode',\n    )\n\n    estimate_sigma = pe.Node(\n        niu.Function(function=_estimate_sigma),\n        name='estimate_sigma',\n    )\n\n    measures = pe.Node(DiffusionQC(), name='measures')\n\n    addprov = pe.Node(\n        AddProvenance(modality='dwi'),\n        name='provenance',\n        run_without_submitting=True,\n    )\n\n    # Save to JSON file\n    datasink = pe.Node(\n        IQMFileSink(\n            modality='dwi',\n            out_dir=str(config.execution.output_dir),\n            dataset=config.execution.dsname,\n        ),\n        name='datasink',\n        run_without_submitting=True,\n    )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, datasink, [('in_file', 'in_file'),\n                               ('entities', 'entities'),\n                               (('metadata', _filter_metadata), 'metadata'),\n                               ('n_shells', 'NumberOfShells'),\n                               ('b_values_shells', 'bValuesEstimation'),\n                               (('b_values_file', _bvals_report), 'bValues')]),\n        (inputnode, measures, [('in_file', 'in_file'),\n                               ('b_values_file', 'in_bval_file'),\n                               ('b_values_shells', 'in_shells_bval'),\n                               ('in_shells', 'in_shells'),\n                               ('in_bvec', 'in_bvec'),\n                               ('in_bvec_rotated', 'in_bvec_rotated'),\n                               ('in_bvec_diff', 'in_bvec_diff'),\n                               ('in_b0', 'in_b0'),\n                               ('brain_mask', 'brain_mask'),\n                               ('wm_mask', 'wm_mask'),\n                               ('cc_mask', 'cc_mask'),\n                               ('spikes_mask', 'spikes_mask'),\n                               ('in_fa', 'in_fa'),\n                               ('in_md', 'in_md'),\n                               ('in_cfa', 'in_cfa'),\n                               ('in_fa_nans', 'in_fa_nans'),\n                               ('in_fa_degenerate', 'in_fa_degenerate'),\n                               ('framewise_displacement', 'in_fd'),\n                               ('qspace_neighbors', 'qspace_neighbors'),\n                               ('piesno_sigma', 'piesno_sigma')]),\n        (inputnode, addprov, [('in_file', 'in_file')]),\n        (addprov, datasink, [('out_prov', 'provenance')]),\n        (datasink, outputnode, [('out_file', 'out_file')]),\n        (measures, datasink, [('out_qc', 'root')]),\n        (inputnode, estimate_sigma, [('in_noise', 'in_file'),\n                                     ('brain_mask', 'mask')]),\n        (estimate_sigma, measures, [('out', 'noise_floor')]),\n        (estimate_sigma, outputnode, [('out', 'noise_floor')]),\n    ])\n    # fmt: on\n    return workflow\n\n\ndef hmc_workflow(name='dMRI_HMC'):\n    \"\"\"\n    Create a :abbr:`HMC (head motion correction)` workflow for dMRI.\n\n    .. workflow::\n\n        from mriqc.workflows.diffusion.base import hmc\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = hmc()\n\n    \"\"\"\n    from nipype.algorithms.confounds import FramewiseDisplacement\n    from nipype.interfaces.afni import Volreg\n\n    from mriqc.interfaces.diffusion import RotateVectors\n\n    workflow = pe.Workflow(name=name)\n\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'in_file',\n                'reference',\n                'in_bvec',\n            ]\n        ),\n        name='inputnode',\n    )\n    outputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'out_file',\n                'out_fd',\n                'out_bvec',\n                'out_bvec_diff',\n            ]\n        ),\n        name='outputnode',\n    )\n\n    # calculate hmc parameters\n    hmc = pe.Node(\n        Volreg(args='-Fourier -twopass', zpad=4, outputtype='NIFTI_GZ'),\n        name='motion_correct',\n        mem_gb=3.0,\n        n_procs=config.nipype.nprocs,\n    )\n\n    bvec_rot = pe.Node(RotateVectors(), name='bvec_rot')\n\n    # Compute the frame-wise displacement\n    fdnode = pe.Node(\n        FramewiseDisplacement(\n            normalize=False,\n            parameter_source='AFNI',\n            radius=config.workflow.fd_radius,\n        ),\n        name='ComputeFD',\n    )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, hmc, [('in_file', 'in_file'),\n                          ('reference', 'basefile')]),\n        (inputnode, bvec_rot, [('in_bvec', 'in_file'),\n                               ('reference', 'reference')]),\n        (hmc, outputnode, [('out_file', 'out_file')]),\n        (hmc, fdnode, [('oned_file', 'in_file')]),\n        (hmc, bvec_rot, [('oned_matrix_save', 'transforms')]),\n        (fdnode, outputnode, [('out_file', 'out_fd')]),\n        (bvec_rot, outputnode, [('out_bvec', 'out_bvec'),\n                                ('out_diff', 'out_bvec_diff')]),\n    ])\n    # fmt: on\n    return workflow\n\n\ndef epi_mni_align(name='SpatialNormalization'):\n    \"\"\"\n    Estimate the transform that maps the EPI space into MNI152NLin2009cAsym.\n\n    The input epi_mean is the averaged and brain-masked EPI timeseries\n\n    Returns the EPI mean resampled in MNI space (for checking out registration) and\n    the associated \"lobe\" parcellation in EPI space.\n\n    .. workflow::\n\n        from mriqc.workflows.diffusion.base import epi_mni_align\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = epi_mni_align()\n\n    \"\"\"\n    from nipype.interfaces.ants import ApplyTransforms, N4BiasFieldCorrection\n    from niworkflows.interfaces.reportlets.registration import (\n        SpatialNormalizationRPT as RobustMNINormalization,\n    )\n    from templateflow.api import get as get_template\n\n    # Get settings\n    testing = config.execution.debug\n    n_procs = config.nipype.nprocs\n    ants_nthreads = config.nipype.omp_nthreads\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(fields=['epi_mean', 'epi_mask']),\n        name='inputnode',\n    )\n    outputnode = pe.Node(\n        niu.IdentityInterface(fields=['epi_mni', 'epi_parc', 'report']),\n        name='outputnode',\n    )\n\n    n4itk = pe.Node(N4BiasFieldCorrection(dimension=3, copy_header=True), name='SharpenEPI')\n\n    norm = pe.Node(\n        RobustMNINormalization(\n            explicit_masking=False,\n            flavor='testing' if testing else 'precise',\n            float=config.execution.ants_float,\n            generate_report=True,\n            moving='boldref',\n            num_threads=ants_nthreads,\n            reference='boldref',\n            template=config.workflow.template_id,\n        ),\n        name='EPI2MNI',\n        num_threads=n_procs,\n        mem_gb=3,\n    )\n\n    if config.workflow.species.lower() == 'human':\n        norm.inputs.reference_image = str(\n            get_template(config.workflow.template_id, resolution=2, suffix='boldref')\n        )\n        norm.inputs.reference_mask = str(\n            get_template(\n                config.workflow.template_id,\n                resolution=2,\n                desc='brain',\n                suffix='mask',\n            )\n        )\n    # adapt some population-specific settings\n    else:\n        from nirodents.workflows.brainextraction import _bspline_grid\n\n        n4itk.inputs.shrink_factor = 1\n        n4itk.inputs.n_iterations = [50] * 4\n        norm.inputs.reference_image = str(get_template(config.workflow.template_id, suffix='T2w'))\n        norm.inputs.reference_mask = str(\n            get_template(\n                config.workflow.template_id,\n                desc='brain',\n                suffix='mask',\n            )[0]\n        )\n\n        bspline_grid = pe.Node(niu.Function(function=_bspline_grid), name='bspline_grid')\n\n        # fmt: off\n        workflow.connect([\n            (inputnode, bspline_grid, [('epi_mean', 'in_file')]),\n            (bspline_grid, n4itk, [('out', 'args')])\n        ])\n        # fmt: on\n\n    # Warp segmentation into EPI space\n    invt = pe.Node(\n        ApplyTransforms(\n            float=True,\n            dimension=3,\n            default_value=0,\n            interpolation='MultiLabel',\n        ),\n        name='ResampleSegmentation',\n    )\n\n    if config.workflow.species.lower() == 'human':\n        invt.inputs.input_image = str(\n            get_template(\n                config.workflow.template_id,\n                resolution=1,\n                desc='carpet',\n                suffix='dseg',\n            )\n        )\n    else:\n        invt.inputs.input_image = str(\n            get_template(\n                config.workflow.template_id,\n                suffix='dseg',\n            )[-1]\n        )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, invt, [('epi_mean', 'reference_image')]),\n        (inputnode, n4itk, [('epi_mean', 'input_image')]),\n        (n4itk, norm, [('output_image', 'moving_image')]),\n        (norm, invt, [\n            ('inverse_composite_transform', 'transforms')]),\n        (invt, outputnode, [('output_image', 'epi_parc')]),\n        (norm, outputnode, [('warped_image', 'epi_mni'),\n                            ('out_report', 'report')]),\n    ])\n    # fmt: on\n\n    if config.workflow.species.lower() == 'human':\n        workflow.connect([(inputnode, norm, [('epi_mask', 'moving_mask')])])\n\n    return workflow\n\n\ndef _mean(inlist):\n    from numpy import mean\n\n    return mean(inlist)\n\n\ndef _parse_tqual(in_file):\n    from numpy import mean\n\n    with open(in_file) as fin:\n        lines = fin.readlines()\n    return mean([float(line.strip()) for line in lines if not line.startswith('++')])\n\n\ndef _parse_tout(in_file):\n    from numpy import loadtxt\n\n    data = loadtxt(in_file)  # pylint: disable=no-member\n    return data.mean()\n\n\ndef _tolist(value):\n    return [value]\n\n\ndef _get_bvals(bmatrix):\n    from numpy import squeeze\n\n    return squeeze(bmatrix[:, -1]).tolist()\n\n\ndef _first(inlist):\n    if isinstance(inlist, (list, tuple)):\n        return inlist[0]\n\n    return inlist\n\n\ndef _all_but_first(inlist):\n    if isinstance(inlist, (list, tuple)):\n        return inlist[1:]\n\n    return inlist\n\n\ndef _estimate_sigma(in_file, mask):\n    import nibabel as nb\n    from numpy import median\n\n    msk = nb.load(mask).get_fdata() > 0.5\n    return round(\n        float(median(nb.load(in_file).get_fdata()[msk])),\n        6,\n    )\n\n\ndef _bvals_report(in_file):\n    import numpy as np\n\n    bvals = [round(float(val), 2) for val in np.unique(np.round(np.loadtxt(in_file), 2))]\n\n    if len(bvals) > 10:\n        return 'Likely DSI'\n\n    return bvals\n\n\ndef _filter_metadata(\n    in_dict,\n    keys=(\n        'global',\n        'dcmmeta_affine',\n        'dcmmeta_reorient_transform',\n        'dcmmeta_shape',\n        'dcmmeta_slice_dim',\n        'dcmmeta_version',\n        'time',\n    ),\n):\n    \"\"\"Drop large and partially redundant objects generated by dcm2niix.\"\"\"\n\n    for key in keys:\n        in_dict.pop(key, None)\n\n    return in_dict\n"
  },
  {
    "path": "mriqc/workflows/diffusion/output.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2023 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Writing out diffusion reportlets.\"\"\"\n\nfrom nipype.interfaces import utility as niu\nfrom nipype.pipeline import engine as pe\nfrom nireports.interfaces.dmri import DWIHeatmap\nfrom nireports.interfaces.reporting.base import (\n    SimpleBeforeAfterRPT as SimpleBeforeAfter,\n)\n\nfrom mriqc import config\nfrom mriqc.interfaces import DerivativesDataSink\n\n\ndef init_dwi_report_wf(name='dwi_report_wf'):\n    \"\"\"\n    Write out individual reportlets.\n\n    .. workflow::\n\n        from mriqc.workflows.diffusion.output import init_dwi_report_wf\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = init_dwi_report_wf()\n\n    \"\"\"\n    from nireports.interfaces import PlotMosaic\n\n    # from mriqc.interfaces.reports import IndividualReport\n\n    verbose = config.execution.verbose_reports\n    mem_gb = config.workflow.biggest_file_gb\n    reportlets_dir = config.execution.work_dir / 'reportlets'\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'in_epi',\n                'brain_mask',\n                'cc_mask',\n                'in_avgmap',\n                'in_stdmap',\n                'in_shells',\n                'in_fa',\n                'in_md',\n                'in_parcellation',\n                'in_bdict',\n                'noise_floor',\n                'name_source',\n            ]\n        ),\n        name='inputnode',\n    )\n\n    # Set FD threshold\n    # inputnode.inputs.fd_thres = config.workflow.fd_thres\n\n    mosaic_fa = pe.Node(\n        PlotMosaic(cmap='Greys_r'),\n        name='mosaic_fa',\n        n_procs=max(1, config.nipype.nprocs // 2),\n    )\n    mosaic_md = pe.Node(\n        PlotMosaic(cmap='Greys_r'),\n        name='mosaic_md',\n        n_procs=max(1, config.nipype.nprocs // 2),\n    )\n\n    mosaic_snr = pe.MapNode(\n        SimpleBeforeAfter(\n            fixed_params={'cmap': 'viridis'},\n            moving_params={'cmap': 'Greys_r'},\n            before_label='Average',\n            after_label='Standard Deviation',\n            dismiss_affine=True,\n        ),\n        name='mosaic_snr',\n        iterfield=['before', 'after'],\n        n_procs=max(1, config.nipype.nprocs // 2),\n    )\n\n    mosaic_noise = pe.MapNode(\n        PlotMosaic(\n            only_noise=True,\n            cmap='viridis_r',\n        ),\n        name='mosaic_noise',\n        iterfield=['in_file'],\n        n_procs=max(1, config.nipype.nprocs // 2),\n    )\n\n    if config.workflow.species.lower() in ('rat', 'mouse'):\n        mosaic_noise.inputs.view = ['coronal', 'axial']\n        mosaic_fa.inputs.view = ['coronal', 'axial']\n        mosaic_md.inputs.view = ['coronal', 'axial']\n\n    ds_report_snr = pe.MapNode(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='avgstd',\n            datatype='figures',\n            allowed_entities=('bval',),\n        ),\n        name='ds_report_snr',\n        run_without_submitting=True,\n        iterfield=['in_file', 'bval'],\n    )\n\n    ds_report_noise = pe.MapNode(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='background',\n            datatype='figures',\n            allowed_entities=('bval',),\n        ),\n        name='ds_report_noise',\n        run_without_submitting=True,\n        iterfield=['in_file', 'bval'],\n    )\n\n    ds_report_fa = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='fa',\n            datatype='figures',\n        ),\n        name='ds_report_fa',\n        run_without_submitting=True,\n    )\n\n    ds_report_md = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='md',\n            datatype='figures',\n        ),\n        name='ds_report_md',\n        run_without_submitting=True,\n    )\n\n    def _gen_entity(inlist):\n        return ['00000'] + [f'{int(round(bval, 0)):05d}' for bval in inlist]\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, mosaic_snr, [('in_avgmap', 'before'),\n                                 ('in_stdmap', 'after'),\n                                 ('brain_mask', 'wm_seg')]),\n        (inputnode, mosaic_noise, [('in_avgmap', 'in_file')]),\n        (inputnode, mosaic_fa, [('in_fa', 'in_file'),\n                                ('brain_mask', 'bbox_mask_file')]),\n        (inputnode, mosaic_md, [('in_md', 'in_file'),\n                                ('brain_mask', 'bbox_mask_file')]),\n        (inputnode, ds_report_snr, [('name_source', 'source_file'),\n                                    (('in_shells', _gen_entity), 'bval')]),\n        (inputnode, ds_report_noise, [('name_source', 'source_file'),\n                                      (('in_shells', _gen_entity), 'bval')]),\n        (inputnode, ds_report_fa, [('name_source', 'source_file')]),\n        (inputnode, ds_report_md, [('name_source', 'source_file')]),\n        (mosaic_snr, ds_report_snr, [('out_report', 'in_file')]),\n        (mosaic_noise, ds_report_noise, [('out_file', 'in_file')]),\n        (mosaic_fa, ds_report_fa, [('out_file', 'in_file')]),\n        (mosaic_md, ds_report_md, [('out_file', 'in_file')]),\n    ])\n    # fmt: on\n\n    get_wm = pe.Node(niu.Function(function=_get_wm), name='get_wm')\n    plot_heatmap = pe.Node(\n        DWIHeatmap(scalarmap_label='Shell-wise Fractional Anisotropy (FA)'),\n        name='plot_heatmap',\n        n_procs=config.nipype.nprocs,\n    )\n    ds_report_hm = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='heatmap',\n            datatype='figures',\n        ),\n        name='ds_report_hm',\n        run_without_submitting=True,\n    )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, get_wm, [('in_parcellation', 'in_file')]),\n        (inputnode, plot_heatmap, [('in_epi', 'in_file'),\n                                   ('in_fa', 'scalarmap'),\n                                   ('in_bdict', 'b_indices'),\n                                   ('noise_floor', 'sigma')]),\n        (inputnode, ds_report_hm, [('name_source', 'source_file')]),\n        (get_wm, plot_heatmap, [('out', 'mask_file')]),\n        (plot_heatmap, ds_report_hm, [('out_file', 'in_file')]),\n\n    ])\n    # fmt: on\n    return workflow\n\n\ndef _carpet_parcellation(segmentation, crown_mask):\n    \"\"\"Generate the union of two masks.\"\"\"\n    from pathlib import Path\n\n    import nibabel as nb\n    import numpy as np\n\n    img = nb.load(segmentation)\n\n    lut = np.zeros((256,), dtype='uint8')\n    lut[100:201] = 1  # Ctx GM\n    lut[30:99] = 2  # dGM\n    lut[1:11] = 3  # WM+CSF\n    lut[255] = 4  # Cerebellum\n    # Apply lookup table\n    seg = lut[np.asanyarray(img.dataobj, dtype='uint16')]\n    seg[np.asanyarray(nb.load(crown_mask).dataobj, dtype=int) > 0] = 5\n\n    outimg = img.__class__(seg.astype('uint8'), img.affine, img.header)\n    outimg.set_data_dtype('uint8')\n    out_file = Path('segments.nii.gz').absolute()\n    outimg.to_filename(out_file)\n    return str(out_file)\n\n\ndef _get_tr(meta_dict):\n    return meta_dict.get('RepetitionTime', None)\n\n\ndef _get_wm(in_file, radius=2):\n    from pathlib import Path\n\n    import nibabel as nb\n    import numpy as np\n    from nipype.utils.filemanip import fname_presuffix\n    from scipy import ndimage as ndi\n    from skimage.morphology import ball\n\n    parc = nb.load(in_file)\n    hdr = parc.header.copy()\n    data = np.array(parc.dataobj, dtype=hdr.get_data_dtype())\n    wm_mask = ndi.binary_erosion((data == 1) | (data == 2), ball(radius))\n\n    hdr.set_data_dtype(np.uint8)\n    out_wm = fname_presuffix(in_file, suffix='wm', newpath=str(Path.cwd()))\n    parc.__class__(\n        wm_mask.astype(np.uint8),\n        parc.affine,\n        hdr,\n    ).to_filename(out_wm)\n    return out_wm\n"
  },
  {
    "path": "mriqc/workflows/functional/__init__.py",
    "content": ""
  },
  {
    "path": "mriqc/workflows/functional/base.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"\nFunctional workflow\n===================\n\n.. image :: _static/functional_workflow_source.svg\n\nThe functional workflow follows the following steps:\n\n#. Sanitize (revise data types and xforms) input data, read\n   associated metadata and discard non-steady state frames.\n#. :abbr:`HMC (head-motion correction)` based on ``3dvolreg`` from\n   AFNI -- :py:func:`hmc`.\n#. Skull-stripping of the time-series (AFNI) --\n   :py:func:`fmri_bmsk_workflow`.\n#. Calculate mean time-series, and :abbr:`tSNR (temporal SNR)`.\n#. Spatial Normalization to MNI (ANTs) -- :py:func:`epi_mni_align`\n#. Extraction of IQMs -- :py:func:`compute_iqms`.\n#. Individual-reports generation --\n   :py:func:`~mriqc.workflows.functional.output.init_func_report_wf`.\n\nThis workflow is orchestrated by :py:func:`fmri_qc_workflow`.\n\"\"\"\n\nfrom nipype.interfaces import utility as niu\nfrom nipype.pipeline import engine as pe\nfrom niworkflows.utils.connections import pop_file as _pop\n\nfrom mriqc import config\nfrom mriqc.workflows.functional.output import init_func_report_wf\n\n\ndef fmri_qc_workflow(name='funcMRIQC'):\n    \"\"\"\n    Initialize the (f)MRIQC workflow.\n\n    .. workflow::\n\n        import os.path as op\n        from mriqc.workflows.functional.base import fmri_qc_workflow\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = fmri_qc_workflow()\n\n    \"\"\"\n    from nipype.algorithms.confounds import TSNR, NonSteadyStateDetector\n    from nipype.interfaces.afni import TStat\n    from niworkflows.interfaces.header import SanitizeImage\n\n    from mriqc.interfaces.functional import SelectEcho\n    from mriqc.messages import BUILDING_WORKFLOW\n\n    mem_gb = config.workflow.biggest_file_gb['bold']\n    dataset = config.workflow.inputs['bold']\n    metadata = config.workflow.inputs_metadata['bold']\n    entities = config.workflow.inputs_entities['bold']\n\n    message = BUILDING_WORKFLOW.format(\n        modality='functional',\n        detail=f'for {len(dataset)} BOLD runs.',\n    )\n    config.loggers.workflow.info(message)\n\n    # Define workflow, inputs and outputs\n    # 0. Get data, put it in RAS orientation\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=['in_file', 'metadata', 'entities'],\n        ),\n        name='inputnode',\n    )\n    inputnode.synchronize = True  # Do not test combinations of iterables\n    inputnode.iterables = [\n        ('in_file', dataset),\n        ('metadata', metadata),\n        ('entities', entities),\n    ]\n\n    outputnode = pe.Node(\n        niu.IdentityInterface(fields=['qc', 'mosaic', 'out_group', 'out_dvars', 'out_fd']),\n        name='outputnode',\n    )\n\n    pick_echo = pe.Node(SelectEcho(), name='pick_echo')\n\n    non_steady_state_detector = pe.Node(NonSteadyStateDetector(), name='non_steady_state_detector')\n\n    sanitize = pe.MapNode(\n        SanitizeImage(max_32bit=config.execution.float32),\n        name='sanitize',\n        mem_gb=mem_gb * 4.0,\n        iterfield=['in_file'],\n    )\n\n    # Workflow --------------------------------------------------------\n\n    # 1. HMC: head motion correct\n    hmcwf = hmc(omp_nthreads=config.nipype.omp_nthreads)\n\n    # Set HMC settings\n    hmcwf.inputs.inputnode.fd_radius = config.workflow.fd_radius\n\n    # 2. Compute mean fmri\n    mean = pe.MapNode(\n        TStat(options='-mean', outputtype='NIFTI_GZ'),\n        name='mean',\n        mem_gb=mem_gb * 1.5,\n        iterfield=['in_file'],\n    )\n\n    # Compute TSNR using nipype implementation\n    tsnr = pe.MapNode(\n        TSNR(),\n        name='compute_tsnr',\n        mem_gb=mem_gb * 2.5,\n        iterfield=['in_file'],\n    )\n\n    # EPI to MNI registration\n    ema = epi_mni_align()\n\n    # 7. Compute IQMs\n    iqmswf = compute_iqms()\n    # Reports\n    func_report_wf = init_func_report_wf()\n\n    # fmt: off\n\n    workflow.connect([\n        (inputnode, pick_echo, [('in_file', 'in_files'),\n                                ('metadata', 'metadata')]),\n        (inputnode, sanitize, [('in_file', 'in_file')]),\n        (pick_echo, non_steady_state_detector, [('out_file', 'in_file')]),\n        (non_steady_state_detector, sanitize, [('n_volumes_to_discard', 'n_volumes_to_discard')]),\n        (sanitize, hmcwf, [('out_file', 'inputnode.in_file')]),\n        (hmcwf, mean, [('outputnode.out_file', 'in_file')]),\n        (hmcwf, tsnr, [('outputnode.out_file', 'in_file')]),\n        (mean, ema, [(('out_file', _pop), 'inputnode.epi_mean')]),\n        # Feed IQMs computation\n        (inputnode, iqmswf, [('in_file', 'inputnode.in_file'),\n                             ('metadata', 'inputnode.metadata'),\n                             ('entities', 'inputnode.entities')]),\n        (sanitize, iqmswf, [('out_file', 'inputnode.in_ras')]),\n        (mean, iqmswf, [('out_file', 'inputnode.epi_mean')]),\n        (hmcwf, iqmswf, [('outputnode.out_file', 'inputnode.hmc_epi'),\n                         ('outputnode.out_fd', 'inputnode.hmc_fd'),\n                         ('outputnode.mpars', 'inputnode.mpars')]),\n        (tsnr, iqmswf, [('tsnr_file', 'inputnode.in_tsnr')]),\n        (non_steady_state_detector, iqmswf, [('n_volumes_to_discard', 'inputnode.exclude_index')]),\n        # Feed reportlet generation\n        (inputnode, func_report_wf, [\n            ('in_file', 'inputnode.name_source'),\n            ('metadata', 'inputnode.meta_sidecar'),\n        ]),\n        (sanitize, func_report_wf, [('out_file', 'inputnode.in_ras')]),\n        (mean, func_report_wf, [('out_file', 'inputnode.epi_mean')]),\n        (tsnr, func_report_wf, [('stddev_file', 'inputnode.in_stddev')]),\n        (hmcwf, func_report_wf, [\n            ('outputnode.out_fd', 'inputnode.hmc_fd'),\n            ('outputnode.out_file', 'inputnode.hmc_epi'),\n        ]),\n        (ema, func_report_wf, [\n            ('outputnode.epi_parc', 'inputnode.epi_parc'),\n            ('outputnode.report', 'inputnode.mni_report'),\n        ]),\n        (iqmswf, func_report_wf, [\n            ('outputnode.out_file', 'inputnode.in_iqms'),\n            ('outputnode.out_dvars', 'inputnode.in_dvars'),\n            ('outputnode.outliers', 'inputnode.outliers'),\n        ]),\n        (hmcwf, outputnode, [('outputnode.out_fd', 'out_fd')]),\n    ])\n    # fmt: on\n\n    if config.workflow.fft_spikes_detector:\n        # fmt: off\n        workflow.connect([\n            (iqmswf, func_report_wf, [\n                ('outputnode.out_spikes', 'inputnode.in_spikes'),\n                ('outputnode.out_fft', 'inputnode.in_fft'),\n            ]),\n        ])\n        # fmt: on\n\n    # population specific changes to brain masking\n    if config.workflow.species == 'human':\n        from mriqc.workflows.shared import synthstrip_wf as fmri_bmsk_workflow\n\n        skullstrip_epi = fmri_bmsk_workflow(omp_nthreads=config.nipype.omp_nthreads)\n        # fmt: off\n        workflow.connect([\n            (mean, skullstrip_epi, [(('out_file', _pop), 'inputnode.in_files')]),\n            (skullstrip_epi, ema, [('outputnode.out_mask', 'inputnode.epi_mask')]),\n            (skullstrip_epi, iqmswf, [('outputnode.out_mask', 'inputnode.brainmask')]),\n            (skullstrip_epi, func_report_wf, [('outputnode.out_mask', 'inputnode.brainmask')]),\n        ])\n        # fmt: on\n    else:\n        from mriqc.workflows.anatomical.base import _binarize\n\n        binarise_labels = pe.Node(\n            niu.Function(\n                input_names=['in_file', 'threshold'],\n                output_names=['out_file'],\n                function=_binarize,\n            ),\n            name='binarise_labels',\n        )\n\n        # fmt: off\n        workflow.connect([\n            (ema, binarise_labels, [('outputnode.epi_parc', 'in_file')]),\n            (binarise_labels, iqmswf, [('out_file', 'inputnode.brainmask')]),\n            (binarise_labels, func_report_wf, [('out_file', 'inputnode.brainmask')])\n        ])\n        # fmt: on\n\n    # Upload metrics\n    if not config.execution.no_sub:\n        from mriqc.interfaces.webapi import UploadIQMs\n\n        upldwf = pe.MapNode(\n            UploadIQMs(\n                endpoint=config.execution.webapi_url,\n                auth_token=config.execution.webapi_token,\n                strict=config.execution.upload_strict,\n            ),\n            name='UploadMetrics',\n            iterfield=['in_iqms'],\n        )\n\n        # fmt: off\n        workflow.connect([\n            (iqmswf, upldwf, [('outputnode.out_file', 'in_iqms')]),\n        ])\n        # fmt: on\n\n    return workflow\n\n\ndef compute_iqms(name='ComputeIQMs'):\n    \"\"\"\n    Initialize the workflow that actually computes the IQMs.\n\n    .. workflow::\n\n        from mriqc.workflows.functional.base import compute_iqms\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = compute_iqms()\n\n    \"\"\"\n    from nipype.algorithms.confounds import ComputeDVARS\n    from nipype.interfaces.afni import OutlierCount, QualityIndex\n\n    from mriqc.interfaces import DerivativesDataSink, FunctionalQC, GatherTimeseries, IQMFileSink\n    from mriqc.interfaces.reports import AddProvenance\n    from mriqc.interfaces.transitional import GCOR\n    from mriqc.workflows.utils import _tofloat, get_fwhmx\n\n    mem_gb = config.workflow.biggest_file_gb['bold']\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'in_file',\n                'metadata',\n                'entities',\n                'in_ras',\n                'epi_mean',\n                'brainmask',\n                'hmc_epi',\n                'hmc_fd',\n                'fd_thres',\n                'in_tsnr',\n                'mpars',\n                'exclude_index',\n            ]\n        ),\n        name='inputnode',\n    )\n    outputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'out_file',\n                'out_dvars',\n                'outliers',\n                'out_spikes',\n                'out_fft',\n            ]\n        ),\n        name='outputnode',\n    )\n\n    # Set FD threshold\n    inputnode.inputs.fd_thres = config.workflow.fd_thres\n\n    # Compute DVARS\n    dvnode = pe.MapNode(\n        ComputeDVARS(save_plot=False, save_all=True),\n        name='ComputeDVARS',\n        mem_gb=mem_gb * 3,\n        iterfield=['in_file'],\n    )\n\n    # AFNI quality measures\n    fwhm = pe.MapNode(get_fwhmx(), name='smoothness', iterfield=['in_file'])\n    fwhm.inputs.acf = True  # Only AFNI >= 16\n\n    outliers = pe.MapNode(\n        OutlierCount(fraction=True, out_file='outliers.out'),\n        name='outliers',\n        mem_gb=mem_gb * 2.5,\n        iterfield=['in_file'],\n    )\n\n    quality = pe.MapNode(\n        QualityIndex(automask=True),\n        out_file='quality.out',\n        name='quality',\n        mem_gb=mem_gb * 3,\n        iterfield=['in_file'],\n    )\n\n    gcor = pe.MapNode(GCOR(), name='gcor', mem_gb=mem_gb * 2, iterfield=['in_file'])\n\n    measures = pe.MapNode(\n        FunctionalQC(),\n        name='measures',\n        mem_gb=mem_gb * 3,\n        n_procs=max(1, config.nipype.nprocs // 2),\n        iterfield=['in_epi', 'in_hmc', 'in_tsnr', 'in_dvars', 'in_fwhm'],\n    )\n\n    timeseries = pe.MapNode(\n        GatherTimeseries(mpars_source='AFNI'),\n        name='timeseries',\n        mem_gb=mem_gb * 3,\n        iterfield=['dvars', 'outliers', 'quality'],\n    )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, dvnode, [('hmc_epi', 'in_file'),\n                             ('brainmask', 'in_mask')]),\n        (inputnode, measures, [('epi_mean', 'in_epi'),\n                               ('brainmask', 'in_mask'),\n                               ('hmc_epi', 'in_hmc'),\n                               ('hmc_fd', 'in_fd'),\n                               ('fd_thres', 'fd_thres'),\n                               ('in_tsnr', 'in_tsnr')]),\n        (inputnode, fwhm, [('epi_mean', 'in_file'),\n                           ('brainmask', 'mask')]),\n        (inputnode, quality, [('hmc_epi', 'in_file')]),\n        (inputnode, outliers, [('hmc_epi', 'in_file'),\n                               ('brainmask', 'mask')]),\n        (inputnode, gcor, [('hmc_epi', 'in_file'),\n                           ('brainmask', 'mask')]),\n        (dvnode, measures, [('out_all', 'in_dvars')]),\n        (fwhm, measures, [(('fwhm', _tofloat), 'in_fwhm')]),\n        (dvnode, outputnode, [('out_all', 'out_dvars')]),\n        (outliers, outputnode, [('out_file', 'outliers')]),\n        (outliers, timeseries, [('out_file', 'outliers')]),\n        (quality, timeseries, [('out_file', 'quality')]),\n        (dvnode, timeseries, [('out_all', 'dvars')]),\n        (inputnode, timeseries, [('hmc_fd', 'fd'), ('mpars', 'mpars')]),\n    ])\n    # fmt: on\n\n    addprov = pe.MapNode(\n        AddProvenance(modality='bold'),\n        name='provenance',\n        run_without_submitting=True,\n        iterfield=['in_file'],\n    )\n\n    # Save to JSON file\n    datasink = pe.MapNode(\n        IQMFileSink(\n            modality='bold',\n            out_dir=str(config.execution.output_dir),\n            dataset=config.execution.dsname,\n        ),\n        name='datasink',\n        run_without_submitting=True,\n        iterfield=['in_file', 'root', 'metadata', 'provenance'],\n    )\n\n    # Save timeseries TSV file\n    ds_timeseries = pe.MapNode(\n        DerivativesDataSink(base_directory=str(config.execution.output_dir), suffix='timeseries'),\n        name='ds_timeseries',\n        run_without_submitting=True,\n        iterfield=['in_file', 'source_file', 'meta_dict'],\n    )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, addprov, [('in_file', 'in_file')]),\n        (inputnode, datasink, [('in_file', 'in_file'),\n                               ('exclude_index', 'dummy_trs'),\n                               ('entities', 'entities'),\n                               ('metadata', 'metadata')]),\n        (addprov, datasink, [('out_prov', 'provenance')]),\n        (outliers, datasink, [(('out_file', _parse_tout), 'aor')]),\n        (gcor, datasink, [(('out', _tofloat), 'gcor')]),\n        (quality, datasink, [(('out_file', _parse_tqual), 'aqi')]),\n        (measures, datasink, [('out_qc', 'root')]),\n        (datasink, outputnode, [('out_file', 'out_file')]),\n        (inputnode, ds_timeseries, [('in_file', 'source_file')]),\n        (timeseries, ds_timeseries, [('timeseries_file', 'in_file'),\n                                     ('timeseries_metadata', 'meta_dict')]),\n    ])\n    # fmt: on\n\n    # FFT spikes finder\n    if config.workflow.fft_spikes_detector:\n        from mriqc.workflows.utils import slice_wise_fft\n\n        spikes_fft = pe.MapNode(\n            niu.Function(\n                input_names=['in_file'],\n                output_names=['n_spikes', 'out_spikes', 'out_fft'],\n                function=slice_wise_fft,\n            ),\n            name='SpikesFinderFFT',\n            iterfield=['in_file'],\n        )\n\n        # fmt: off\n        workflow.connect([\n            (inputnode, spikes_fft, [('in_ras', 'in_file')]),\n            (spikes_fft, outputnode, [('out_spikes', 'out_spikes'),\n                                      ('out_fft', 'out_fft')]),\n            (spikes_fft, datasink, [('n_spikes', 'spikes_num')])\n        ])\n        # fmt: on\n\n    return workflow\n\n\ndef fmri_bmsk_workflow(name='fMRIBrainMask'):\n    \"\"\"\n    Compute a brain mask for the input :abbr:`fMRI (functional MRI)` dataset.\n\n    .. workflow::\n\n        from mriqc.workflows.functional.base import fmri_bmsk_workflow\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = fmri_bmsk_workflow()\n\n\n    \"\"\"\n    from nipype.interfaces.afni import Automask\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(niu.IdentityInterface(fields=['in_file']), name='inputnode')\n    outputnode = pe.Node(niu.IdentityInterface(fields=['out_file']), name='outputnode')\n    afni_msk = pe.Node(Automask(outputtype='NIFTI_GZ'), name='afni_msk')\n\n    # Connect brain mask extraction\n    # fmt: off\n    workflow.connect([\n        (inputnode, afni_msk, [('in_file', 'in_file')]),\n        (afni_msk, outputnode, [('out_file', 'out_file')])\n    ])\n    # fmt: on\n    return workflow\n\n\ndef hmc(name='fMRI_HMC', omp_nthreads=None):\n    \"\"\"\n    Create a :abbr:`HMC (head motion correction)` workflow for fMRI.\n\n    .. workflow::\n\n        from mriqc.workflows.functional.base import hmc\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = hmc()\n\n    \"\"\"\n    from nipype.algorithms.confounds import FramewiseDisplacement\n    from nipype.interfaces.afni import Despike, Refit, Volreg\n\n    mem_gb = config.workflow.biggest_file_gb['bold']\n\n    workflow = pe.Workflow(name=name)\n\n    inputnode = pe.Node(\n        niu.IdentityInterface(fields=['in_file', 'fd_radius']),\n        name='inputnode',\n    )\n\n    outputnode = pe.Node(\n        niu.IdentityInterface(fields=['out_file', 'out_fd', 'mpars']),\n        name='outputnode',\n    )\n\n    # calculate hmc parameters\n    estimate_hm = pe.Node(\n        Volreg(args='-Fourier -twopass', zpad=4, outputtype='NIFTI_GZ'),\n        name='estimate_hm',\n        mem_gb=mem_gb * 2.5,\n    )\n\n    # Compute the frame-wise displacement\n    fdnode = pe.Node(\n        FramewiseDisplacement(normalize=False, parameter_source='AFNI'),\n        name='ComputeFD',\n    )\n\n    # Apply transforms to other echos\n    apply_hmc = pe.MapNode(\n        niu.Function(\n            function=_apply_transforms,\n            input_names=['in_file', 'in_xfm', 'max_concurrent'],\n        ),\n        name='apply_hmc',\n        iterfield=['in_file'],\n        # NiTransforms is a memory hog, so ensure only one process is running at a time\n        n_procs=config.environment.cpu_count,\n    )\n    apply_hmc.inputs.max_concurrent = 4\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, fdnode, [('fd_radius', 'radius')]),\n        (estimate_hm, apply_hmc, [('oned_matrix_save', 'in_xfm')]),\n        (apply_hmc, outputnode, [('out', 'out_file')]),\n        (estimate_hm, fdnode, [('oned_file', 'in_file')]),\n        (estimate_hm, outputnode, [('oned_file', 'mpars')]),\n        (fdnode, outputnode, [('out_file', 'out_fd')]),\n    ])\n    # fmt: on\n\n    if not (config.workflow.despike or config.workflow.deoblique):\n        # fmt: off\n        workflow.connect([\n            (inputnode, estimate_hm, [(('in_file', _pop), 'in_file')]),\n            (inputnode, apply_hmc, [('in_file', 'in_file')]),\n        ])\n        # fmt: on\n        return workflow\n\n    # despiking, and deoblique\n    deoblique_node = pe.MapNode(\n        Refit(deoblique=True),\n        name='deoblique',\n        iterfield=['in_file'],\n    )\n    despike_node = pe.MapNode(\n        Despike(outputtype='NIFTI_GZ'),\n        name='despike',\n        iterfield=['in_file'],\n    )\n    if config.workflow.despike and config.workflow.deoblique:\n        # fmt: off\n        workflow.connect([\n            (inputnode, despike_node, [('in_file', 'in_file')]),\n            (despike_node, deoblique_node, [('out_file', 'in_file')]),\n            (deoblique_node, estimate_hm, [(('out_file', _pop), 'in_file')]),\n            (deoblique_node, apply_hmc, [('out_file', 'in_file')]),\n        ])\n        # fmt: on\n    elif config.workflow.despike:\n        # fmt: off\n        workflow.connect([\n            (inputnode, despike_node, [('in_file', 'in_file')]),\n            (despike_node, estimate_hm, [(('out_file', _pop), 'in_file')]),\n            (despike_node, apply_hmc, [('out_file', 'in_file')]),\n        ])\n        # fmt: on\n    elif config.workflow.deoblique:\n        # fmt: off\n        workflow.connect([\n            (inputnode, deoblique_node, [('in_file', 'in_file')]),\n            (deoblique_node, estimate_hm, [(('out_file', _pop), 'in_file')]),\n            (deoblique_node, apply_hmc, [('out_file', 'in_file')]),\n        ])\n        # fmt: on\n    else:\n        raise NotImplementedError\n\n    return workflow\n\n\ndef epi_mni_align(name='SpatialNormalization'):\n    \"\"\"\n    Estimate the transform that maps the EPI space into MNI152NLin2009cAsym.\n\n    The input epi_mean is the averaged and brain-masked EPI timeseries\n\n    Returns the EPI mean resampled in MNI space (for checking out registration) and\n    the associated \"lobe\" parcellation in EPI space.\n\n    .. workflow::\n\n        from mriqc.workflows.functional.base import epi_mni_align\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = epi_mni_align()\n\n    \"\"\"\n    from nipype.interfaces.ants import ApplyTransforms, N4BiasFieldCorrection\n    from niworkflows.interfaces.reportlets.registration import (\n        SpatialNormalizationRPT as RobustMNINormalization,\n    )\n    from templateflow.api import get as get_template\n\n    # Get settings\n    testing = config.execution.debug\n    n_procs = config.nipype.nprocs\n    ants_nthreads = config.nipype.omp_nthreads\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(fields=['epi_mean', 'epi_mask']),\n        name='inputnode',\n    )\n    outputnode = pe.Node(\n        niu.IdentityInterface(fields=['epi_mni', 'epi_parc', 'report']),\n        name='outputnode',\n    )\n\n    n4itk = pe.Node(N4BiasFieldCorrection(dimension=3, copy_header=True), name='SharpenEPI')\n\n    norm = pe.Node(\n        RobustMNINormalization(\n            explicit_masking=False,\n            flavor='testing' if testing else 'precise',\n            float=config.execution.ants_float,\n            generate_report=True,\n            moving='boldref',\n            num_threads=ants_nthreads,\n            reference='boldref',\n            template=config.workflow.template_id,\n        ),\n        name='EPI2MNI',\n        num_threads=n_procs,\n        mem_gb=3,\n    )\n\n    if config.workflow.species.lower() == 'human':\n        norm.inputs.reference_image = str(\n            get_template(config.workflow.template_id, resolution=2, suffix='boldref')\n        )\n        norm.inputs.reference_mask = str(\n            get_template(\n                config.workflow.template_id,\n                resolution=2,\n                desc='brain',\n                suffix='mask',\n            )\n        )\n    # adapt some population-specific settings\n    else:\n        from nirodents.workflows.brainextraction import _bspline_grid\n\n        n4itk.inputs.shrink_factor = 1\n        n4itk.inputs.n_iterations = [50] * 4\n        norm.inputs.reference_image = str(get_template(config.workflow.template_id, suffix='T2w'))\n        norm.inputs.reference_mask = str(\n            get_template(\n                config.workflow.template_id,\n                desc='brain',\n                suffix='mask',\n            )[0]\n        )\n\n        bspline_grid = pe.Node(niu.Function(function=_bspline_grid), name='bspline_grid')\n\n        # fmt: off\n        workflow.connect([\n            (inputnode, bspline_grid, [('epi_mean', 'in_file')]),\n            (bspline_grid, n4itk, [('out', 'args')])\n        ])\n        # fmt: on\n\n    # Warp segmentation into EPI space\n    invt = pe.Node(\n        ApplyTransforms(\n            float=True,\n            dimension=3,\n            default_value=0,\n            interpolation='MultiLabel',\n        ),\n        name='ResampleSegmentation',\n    )\n\n    if config.workflow.species.lower() == 'human':\n        invt.inputs.input_image = str(\n            get_template(\n                config.workflow.template_id,\n                resolution=1,\n                desc='carpet',\n                suffix='dseg',\n            )\n        )\n    else:\n        invt.inputs.input_image = str(\n            get_template(\n                config.workflow.template_id,\n                suffix='dseg',\n            )[-1]\n        )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, invt, [('epi_mean', 'reference_image')]),\n        (inputnode, n4itk, [('epi_mean', 'input_image')]),\n        (n4itk, norm, [('output_image', 'moving_image')]),\n        (norm, invt, [\n            ('inverse_composite_transform', 'transforms')]),\n        (invt, outputnode, [('output_image', 'epi_parc')]),\n        (norm, outputnode, [('warped_image', 'epi_mni'),\n                            ('out_report', 'report')]),\n    ])\n    # fmt: on\n\n    if config.workflow.species.lower() == 'human':\n        workflow.connect([(inputnode, norm, [('epi_mask', 'moving_mask')])])\n\n    return workflow\n\n\ndef _parse_tqual(in_file):\n    if isinstance(in_file, (list, tuple)):\n        return [_parse_tqual(f) for f in in_file] if len(in_file) > 1 else _parse_tqual(in_file[0])\n\n    import numpy as np\n\n    with open(in_file) as fin:\n        lines = fin.readlines()\n    return np.mean([float(line.strip()) for line in lines if not line.startswith('++')])\n\n\ndef _parse_tout(in_file):\n    if isinstance(in_file, (list, tuple)):\n        return [_parse_tout(f) for f in in_file] if len(in_file) > 1 else _parse_tout(in_file[0])\n\n    import numpy as np\n\n    data = np.loadtxt(in_file)  # pylint: disable=no-member\n    return data.mean()\n\n\ndef _apply_transforms(in_file, in_xfm, max_concurrent):\n    from pathlib import Path\n\n    from nitransforms.linear import load\n    from nitransforms.resampling import apply\n\n    from mriqc.utils.bids import derive_bids_fname\n\n    realigned = apply(\n        load(in_xfm, fmt='afni', reference=in_file, moving=in_file),\n        in_file,\n        dtype_width=4,\n        serialize_nvols=2,\n        max_concurrent=max_concurrent,\n        mode='reflect',\n    )\n    out_file = derive_bids_fname(\n        in_file,\n        entity='desc-realigned',\n        newpath=Path.cwd(),\n        absolute=True,\n    )\n\n    realigned.to_filename(out_file)\n    return str(out_file)\n"
  },
  {
    "path": "mriqc/workflows/functional/output.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Writing out functional reportlets.\"\"\"\n\nfrom nipype.interfaces import utility as niu\nfrom nipype.pipeline import engine as pe\n\nfrom mriqc import config\nfrom mriqc.interfaces import DerivativesDataSink\n\n\ndef init_func_report_wf(name='func_report_wf'):\n    \"\"\"\n    Write out individual reportlets.\n\n    .. workflow::\n\n        from mriqc.workflows.functional.output import init_func_report_wf\n        from mriqc.testing import mock_config\n        with mock_config():\n            wf = init_func_report_wf()\n\n    \"\"\"\n    from nireports.interfaces import FMRISummary, PlotMosaic, PlotSpikes\n    from niworkflows.interfaces.morphology import BinaryDilation, BinarySubtraction\n\n    from mriqc.interfaces.functional import Spikes\n\n    # from mriqc.interfaces.reports import IndividualReport\n\n    verbose = config.execution.verbose_reports\n    mem_gb = config.workflow.biggest_file_gb['bold']\n    reportlets_dir = config.execution.work_dir / 'reportlets'\n\n    workflow = pe.Workflow(name=name)\n    inputnode = pe.Node(\n        niu.IdentityInterface(\n            fields=[\n                'in_ras',\n                'hmc_epi',\n                'epi_mean',\n                'brainmask',\n                'hmc_fd',\n                'fd_thres',\n                'epi_parc',\n                'in_dvars',\n                'in_stddev',\n                'outliers',\n                'in_spikes',\n                'in_fft',\n                'in_iqms',\n                'mni_report',\n                'ica_report',\n                'meta_sidecar',\n                'name_source',\n            ]\n        ),\n        name='inputnode',\n    )\n\n    # Set FD threshold\n    inputnode.inputs.fd_thres = config.workflow.fd_thres\n\n    spmask = pe.MapNode(\n        niu.Function(\n            input_names=['in_file', 'in_mask'],\n            output_names=['out_file', 'out_plot'],\n            function=spikes_mask,\n        ),\n        name='SpikesMask',\n        mem_gb=mem_gb * 3.5,\n        iterfield=['in_file'],\n    )\n\n    spikes_bg = pe.MapNode(\n        Spikes(no_zscore=True, detrend=False),\n        name='SpikesFinderBgMask',\n        mem_gb=mem_gb * 2.5,\n        iterfield=['in_file', 'in_mask'],\n        n_procs=(config.nipype.nprocs + 3) // 4,  # spikes is a memory hog\n    )\n\n    # Generate crown mask\n    # Create the crown mask\n    dilated_mask = pe.Node(BinaryDilation(), name='dilated_mask')\n    subtract_mask = pe.Node(BinarySubtraction(), name='subtract_mask')\n    parcels = pe.Node(niu.Function(function=_carpet_parcellation), name='parcels')\n\n    bigplot = pe.MapNode(\n        FMRISummary(),\n        name='BigPlot',\n        mem_gb=mem_gb * 3.5,\n        iterfield=['in_func', 'dvars', 'outliers', 'in_spikes_bg'],\n        n_procs=(config.nipype.nprocs + 3) // 4,  # Big plot is a memory hog\n    )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, spikes_bg, [('in_ras', 'in_file')]),\n        (inputnode, spmask, [('in_ras', 'in_file')]),\n        (inputnode, bigplot, [('hmc_epi', 'in_func'),\n                              ('hmc_fd', 'fd'),\n                              ('fd_thres', 'fd_thres'),\n                              ('in_dvars', 'dvars'),\n                              ('outliers', 'outliers'),\n                              (('meta_sidecar', _get_tr), 'tr')]),\n        (inputnode, parcels, [('epi_parc', 'segmentation')]),\n        (inputnode, dilated_mask, [('brainmask', 'in_mask')]),\n        (inputnode, subtract_mask, [('brainmask', 'in_subtract')]),\n        (spmask, spikes_bg, [('out_file', 'in_mask')]),\n        (dilated_mask, subtract_mask, [('out_mask', 'in_base')]),\n        (subtract_mask, parcels, [('out_mask', 'crown_mask')]),\n        (parcels, bigplot, [('out', 'in_segm')]),\n        (spikes_bg, bigplot, [('out_tsz', 'in_spikes_bg')]),\n    ])\n    # fmt: on\n\n    mosaic_mean = pe.MapNode(\n        PlotMosaic(\n            out_file='plot_func_mean_mosaic1.svg',\n            cmap='Greys_r',\n        ),\n        name='PlotMosaicMean',\n        iterfield=['in_file'],\n    )\n\n    mosaic_stddev = pe.MapNode(\n        PlotMosaic(\n            out_file='plot_func_stddev_mosaic2_stddev.svg',\n            cmap='viridis',\n        ),\n        name='PlotMosaicSD',\n        iterfield=['in_file'],\n    )\n\n    mosaic_zoom = pe.MapNode(\n        PlotMosaic(\n            cmap='Greys_r',\n        ),\n        name='PlotMosaicZoomed',\n        iterfield=['in_file'],\n    )\n\n    mosaic_noise = pe.MapNode(\n        PlotMosaic(\n            only_noise=True,\n            cmap='viridis_r',\n        ),\n        name='PlotMosaicNoise',\n        iterfield=['in_file'],\n    )\n\n    if config.workflow.species.lower() in ('rat', 'mouse'):\n        mosaic_mean.inputs.view = ['coronal', 'axial']\n        mosaic_stddev.inputs.view = ['coronal', 'axial']\n        mosaic_zoom.inputs.view = ['coronal', 'axial']\n        mosaic_noise.inputs.view = ['coronal', 'axial']\n\n    ds_report_mean = pe.MapNode(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='mean',\n            datatype='figures',\n            dismiss_entities=('part',),\n        ),\n        name='ds_report_mean',\n        run_without_submitting=True,\n        iterfield=['in_file', 'source_file'],\n    )\n\n    ds_report_stdev = pe.MapNode(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='stdev',\n            datatype='figures',\n            dismiss_entities=('part',),\n        ),\n        name='ds_report_stdev',\n        run_without_submitting=True,\n        iterfield=['in_file', 'source_file'],\n    )\n\n    ds_report_background = pe.MapNode(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='background',\n            datatype='figures',\n            dismiss_entities=('part',),\n        ),\n        name='ds_report_background',\n        run_without_submitting=True,\n        iterfield=['in_file', 'source_file'],\n    )\n\n    ds_report_zoomed = pe.MapNode(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='zoomed',\n            datatype='figures',\n            dismiss_entities=('part',),\n        ),\n        name='ds_report_zoomed',\n        run_without_submitting=True,\n        iterfield=['in_file', 'source_file'],\n    )\n\n    ds_report_carpet = pe.MapNode(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='carpet',\n            datatype='figures',\n            dismiss_entities=('part',),\n        ),\n        name='ds_report_carpet',\n        run_without_submitting=True,\n        iterfield=['in_file', 'source_file'],\n    )\n\n    # fmt: off\n    workflow.connect([\n        # (inputnode, rnode, [(\"in_iqms\", \"in_iqms\")]),\n        (inputnode, mosaic_mean, [('epi_mean', 'in_file')]),\n        (inputnode, mosaic_stddev, [('in_stddev', 'in_file')]),\n        (inputnode, ds_report_mean, [('name_source', 'source_file')]),\n        (inputnode, ds_report_stdev, [('name_source', 'source_file')]),\n        (inputnode, ds_report_background, [('name_source', 'source_file')]),\n        (inputnode, ds_report_zoomed, [('name_source', 'source_file')]),\n        (inputnode, ds_report_carpet, [('name_source', 'source_file')]),\n        (inputnode, mosaic_zoom, [('epi_mean', 'in_file'),\n                                  ('brainmask', 'bbox_mask_file')]),\n        (inputnode, mosaic_noise, [('epi_mean', 'in_file')]),\n        (mosaic_mean, ds_report_mean, [('out_file', 'in_file')]),\n        (mosaic_stddev, ds_report_stdev, [('out_file', 'in_file')]),\n        (mosaic_noise, ds_report_background, [('out_file', 'in_file')]),\n        (mosaic_zoom, ds_report_zoomed, [('out_file', 'in_file')]),\n        (bigplot, ds_report_carpet, [('out_file', 'in_file')]),\n    ])\n    # fmt: on\n\n    if config.workflow.fft_spikes_detector:\n        mosaic_spikes = pe.Node(\n            PlotSpikes(\n                out_file='plot_spikes.svg',\n                cmap='viridis',\n                title='High-Frequency spikes',\n            ),\n            name='PlotSpikes',\n        )\n\n        ds_report_spikes = pe.Node(\n            DerivativesDataSink(\n                base_directory=reportlets_dir,\n                desc='spikes',\n                datatype='figures',\n                dismiss_entities=('part',),\n            ),\n            name='ds_report_spikes',\n            run_without_submitting=True,\n        )\n\n        # fmt: off\n        workflow.connect([\n            (inputnode, ds_report_spikes, [('name_source', 'source_file')]),\n            (inputnode, mosaic_spikes, [('in_ras', 'in_file'),\n                                        ('in_spikes', 'in_spikes'),\n                                        ('in_fft', 'in_fft')]),\n            (mosaic_spikes, ds_report_spikes, [('out_file', 'in_file')]),\n        ])\n        # fmt: on\n\n    if not verbose:\n        return workflow\n\n    # Verbose-reporting goes here\n    from nireports.interfaces import PlotContours\n    from niworkflows.utils.connections import pop_file as _pop\n\n    plot_bmask = pe.Node(\n        PlotContours(\n            display_mode='y' if config.workflow.species.lower() in ('rat', 'mouse') else 'z',\n            levels=[0.5],\n            colors=['r'],\n            cut_coords=10,\n            out_file='bmask',\n        ),\n        name='PlotBrainmask',\n    )\n\n    ds_report_bmask = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='brainmask',\n            datatype='figures',\n            dismiss_entities=('part', 'echo'),\n        ),\n        name='ds_report_bmask',\n        run_without_submitting=True,\n    )\n\n    ds_report_norm = pe.Node(\n        DerivativesDataSink(\n            base_directory=reportlets_dir,\n            desc='norm',\n            datatype='figures',\n            dismiss_entities=('part', 'echo'),\n        ),\n        name='ds_report_norm',\n        run_without_submitting=True,\n    )\n\n    # fmt: off\n    workflow.connect([\n        (inputnode, ds_report_norm, [('mni_report', 'in_file'),\n                                     ('name_source', 'source_file')]),\n        (inputnode, plot_bmask, [(('epi_mean', _pop), 'in_file'),\n                                 ('brainmask', 'in_contours')]),\n        (inputnode, ds_report_bmask, [('name_source', 'source_file')]),\n        (plot_bmask, ds_report_bmask, [(('out_file', _pop), 'in_file')]),\n    ])\n    # fmt: on\n\n    return workflow\n\n\ndef spikes_mask(in_file, in_mask=None, out_file=None):\n    \"\"\"Calculate a mask in which check for :abbr:`EM (electromagnetic)` spikes.\"\"\"\n    import os.path as op\n\n    import nibabel as nb\n    import numpy as np\n    from nilearn.image import mean_img\n    from nilearn.plotting import plot_roi\n    from scipy import ndimage as nd\n\n    if out_file is None:\n        fname, ext = op.splitext(op.basename(in_file))\n        if ext == '.gz':\n            fname, ext2 = op.splitext(fname)\n            ext = ext2 + ext\n        out_file = op.abspath(f'{fname}_spmask{ext}')\n        out_plot = op.abspath(f'{fname}_spmask.pdf')\n\n    in_4d_nii = nb.load(in_file)\n    orientation = nb.aff2axcodes(in_4d_nii.affine)\n\n    if in_mask:\n        mask_data = np.asanyarray(nb.load(in_mask).dataobj)\n        a = np.where(mask_data != 0)\n        bbox = (\n            np.max(a[0]) - np.min(a[0]),\n            np.max(a[1]) - np.min(a[1]),\n            np.max(a[2]) - np.min(a[2]),\n        )\n        longest_axis = np.argmax(bbox)\n\n        # Input here is a binarized and intersected mask data from previous section\n        dil_mask = nd.binary_dilation(mask_data, iterations=int(mask_data.shape[longest_axis] / 9))\n\n        rep = list(mask_data.shape)\n        rep[longest_axis] = -1\n        new_mask_2d = dil_mask.max(axis=longest_axis).reshape(rep)\n\n        rep = [1, 1, 1]\n        rep[longest_axis] = mask_data.shape[longest_axis]\n        new_mask_3d = np.logical_not(np.tile(new_mask_2d, rep))\n    else:\n        new_mask_3d = np.zeros(in_4d_nii.shape[:3]) == 1\n\n    if orientation[0] in ('L', 'R'):\n        new_mask_3d[0:2, :, :] = True\n        new_mask_3d[-3:-1, :, :] = True\n    else:\n        new_mask_3d[:, 0:2, :] = True\n        new_mask_3d[:, -3:-1, :] = True\n\n    mask_nii = nb.Nifti1Image(new_mask_3d.astype(np.uint8), in_4d_nii.affine, in_4d_nii.header)\n    mask_nii.to_filename(out_file)\n\n    plot_roi(mask_nii, mean_img(in_4d_nii), output_file=out_plot)\n    return out_file, out_plot\n\n\ndef _carpet_parcellation(segmentation, crown_mask):\n    \"\"\"Generate the union of two masks.\"\"\"\n    from pathlib import Path\n\n    import nibabel as nb\n    import numpy as np\n\n    img = nb.load(segmentation)\n\n    lut = np.zeros((256,), dtype='uint8')\n    lut[100:201] = 1  # Ctx GM\n    lut[30:99] = 2  # dGM\n    lut[1:11] = 3  # WM+CSF\n    lut[255] = 4  # Cerebellum\n    # Apply lookup table\n    seg = lut[np.asanyarray(img.dataobj, dtype='uint16')]\n    seg[np.asanyarray(nb.load(crown_mask).dataobj, dtype=int) > 0] = 5\n\n    outimg = img.__class__(seg.astype('uint8'), img.affine, img.header)\n    outimg.set_data_dtype('uint8')\n    out_file = Path('segments.nii.gz').absolute()\n    outimg.to_filename(out_file)\n    return str(out_file)\n\n\ndef _get_tr(meta_dict):\n    if isinstance(meta_dict, (list, tuple)):\n        meta_dict = meta_dict[0]\n\n    return meta_dict.get('RepetitionTime', None)\n"
  },
  {
    "path": "mriqc/workflows/shared.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2023 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Shared workflows.\"\"\"\n\nfrom nipype.interfaces import utility as niu\nfrom nipype.pipeline import engine as pe\n\n\ndef synthstrip_wf(name='synthstrip_wf', omp_nthreads=None):\n    \"\"\"Create a brain-extraction workflow using SynthStrip.\"\"\"\n    from nipype.interfaces.ants import N4BiasFieldCorrection\n    from niworkflows.interfaces.nibabel import ApplyMask, IntensityClip\n\n    from mriqc.interfaces.synthstrip import SynthStrip\n\n    inputnode = pe.Node(niu.IdentityInterface(fields=['in_files']), name='inputnode')\n    outputnode = pe.Node(\n        niu.IdentityInterface(fields=['out_corrected', 'out_brain', 'bias_image', 'out_mask']),\n        name='outputnode',\n    )\n\n    # truncate target intensity for N4 correction\n    pre_clip = pe.Node(IntensityClip(p_min=10, p_max=99.9), name='pre_clip')\n\n    pre_n4 = pe.Node(\n        N4BiasFieldCorrection(\n            dimension=3,\n            num_threads=omp_nthreads,\n            rescale_intensities=True,\n            copy_header=True,\n        ),\n        name='pre_n4',\n    )\n\n    post_n4 = pe.Node(\n        N4BiasFieldCorrection(\n            dimension=3,\n            save_bias=True,\n            num_threads=omp_nthreads,\n            n_iterations=[50] * 4,\n            copy_header=True,\n        ),\n        name='post_n4',\n    )\n\n    synthstrip = pe.Node(\n        SynthStrip(num_threads=omp_nthreads),\n        name='synthstrip',\n        num_threads=omp_nthreads,\n    )\n\n    final_masked = pe.Node(ApplyMask(), name='final_masked')\n\n    workflow = pe.Workflow(name=name)\n    # fmt: off\n    workflow.connect([\n        (inputnode, pre_clip, [('in_files', 'in_file')]),\n        (pre_clip, pre_n4, [('out_file', 'input_image')]),\n        (pre_n4, synthstrip, [('output_image', 'in_file')]),\n        (synthstrip, post_n4, [('out_mask', 'weight_image')]),\n        (synthstrip, final_masked, [('out_mask', 'in_mask')]),\n        (pre_clip, post_n4, [('out_file', 'input_image')]),\n        (post_n4, final_masked, [('output_image', 'in_file')]),\n        (final_masked, outputnode, [('out_file', 'out_brain')]),\n        (post_n4, outputnode, [('bias_image', 'bias_image')]),\n        (synthstrip, outputnode, [('out_mask', 'out_mask')]),\n        (post_n4, outputnode, [('output_image', 'out_corrected')]),\n    ])\n    # fmt: on\n    return workflow\n"
  },
  {
    "path": "mriqc/workflows/utils.py",
    "content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n#\n# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# We support and encourage derived works from this project, please read\n# about our expectations at\n#\n#     https://www.nipreps.org/community/licensing/\n#\n\"\"\"Helper functions for the workflows.\"\"\"\n\n\ndef _tofloat(inlist):\n    if isinstance(inlist, (list, tuple)):\n        return [_tofloat(el) for el in inlist] if len(inlist) > 1 else _tofloat(inlist[0])\n    return float(inlist)\n\n\ndef fwhm_dict(fwhm):\n    \"\"\"Convert a list of FWHM into a dictionary\"\"\"\n    fwhm = [float(f) for f in fwhm]\n    return {\n        'fwhm_x': fwhm[0],\n        'fwhm_y': fwhm[1],\n        'fwhm_z': fwhm[2],\n        'fwhm_avg': fwhm[3],\n    }\n\n\ndef thresh_image(in_file, thres=0.5, out_file=None):\n    \"\"\"Thresholds an image\"\"\"\n    import os.path as op\n\n    import nibabel as nb\n    import numpy as np\n\n    if out_file is None:\n        fname, ext = op.splitext(op.basename(in_file))\n        if ext == '.gz':\n            fname, ext2 = op.splitext(fname)\n            ext = ext2 + ext\n        out_file = op.abspath(f'{fname}_thresh{ext}')\n\n    im = nb.load(in_file)\n    data = np.asanyarray(im.dataobj)\n    data[data < thres] = 0\n    data[data > 0] = 1\n    nb.Nifti1Image(data, im.affine, im.header).to_filename(out_file)\n    return out_file\n\n\ndef spectrum_mask(size):\n    \"\"\"Creates a mask to filter the image of size size\"\"\"\n    import numpy as np\n    from scipy.ndimage.morphology import distance_transform_edt as distance\n\n    ftmask = np.ones(size)\n\n    # Set zeros on corners\n    # ftmask[0, 0] = 0\n    # ftmask[size[0] - 1, size[1] - 1] = 0\n    # ftmask[0, size[1] - 1] = 0\n    # ftmask[size[0] - 1, 0] = 0\n    ftmask[size[0] // 2, size[1] // 2] = 0\n\n    # Distance transform\n    ftmask = distance(ftmask)\n    ftmask /= ftmask.max()\n\n    # Keep this just in case we want to switch to the opposite filter\n    ftmask *= -1.0\n    ftmask += 1.0\n\n    ftmask[ftmask >= 0.4] = 1\n    ftmask[ftmask < 1] = 0\n    return ftmask\n\n\ndef slice_wise_fft(in_file, ftmask=None, spike_thres=3.0, out_prefix=None):\n    \"\"\"Search for spikes in slices using the 2D FFT\"\"\"\n    import os.path as op\n\n    import nibabel as nb\n    import numpy as np\n    from scipy.ndimage import binary_erosion, generate_binary_structure\n    from scipy.ndimage.filters import median_filter\n    from statsmodels.robust.scale import mad\n\n    from mriqc.workflows.utils import spectrum_mask\n\n    if out_prefix is None:\n        fname, ext = op.splitext(op.basename(in_file))\n        if ext == '.gz':\n            fname, _ = op.splitext(fname)\n        out_prefix = op.abspath(fname)\n\n    func_data = nb.load(in_file).get_fdata()\n\n    if ftmask is None:\n        ftmask = spectrum_mask(tuple(func_data.shape[:2]))\n\n    fft_data = []\n    for t in range(func_data.shape[-1]):\n        func_frame = func_data[..., t]\n        fft_slices = []\n        for z in range(func_frame.shape[2]):\n            sl = func_frame[..., z]\n            fftsl = (\n                median_filter(\n                    np.real(np.fft.fft2(sl)).astype(np.float32),\n                    size=(5, 5),\n                    mode='constant',\n                )\n                * ftmask\n            )\n            fft_slices.append(fftsl)\n        fft_data.append(np.stack(fft_slices, axis=-1))\n\n    # Recompose the 4D FFT timeseries\n    fft_data = np.stack(fft_data, -1)\n\n    # Z-score across t, using robust statistics\n    mu = np.median(fft_data, axis=3)\n    sigma = np.stack([mad(fft_data, axis=3)] * fft_data.shape[-1], -1)\n    idxs = np.where(np.abs(sigma) > 1e-4)\n    fft_zscored = fft_data - mu[..., np.newaxis]\n    fft_zscored[idxs] /= sigma[idxs]\n\n    # save fft z-scored\n    out_fft = op.abspath(out_prefix + '_zsfft.nii.gz')\n    nii = nb.Nifti1Image(fft_zscored.astype(np.float32), np.eye(4), None)\n    nii.to_filename(out_fft)\n\n    # Find peaks\n    spikes_list = []\n    for t in range(fft_zscored.shape[-1]):\n        fft_frame = fft_zscored[..., t]\n\n        for z in range(fft_frame.shape[-1]):\n            sl = fft_frame[..., z]\n            if np.all(sl < spike_thres):\n                continue\n\n            # Any zscore over spike_thres will be called a spike\n            sl[sl <= spike_thres] = 0\n            sl[sl > 0] = 1\n\n            # Erode peaks and see how many survive\n            struct = generate_binary_structure(2, 2)\n            sl = binary_erosion(sl.astype(np.uint8), structure=struct).astype(np.uint8)\n\n            if sl.sum() > 10:\n                spikes_list.append((t, z))\n\n    out_spikes = op.abspath(out_prefix + '_spikes.tsv')\n    np.savetxt(out_spikes, spikes_list, fmt=b'%d', delimiter=b'\\t', header='TR\\tZ')\n\n    return len(spikes_list), out_spikes, out_fft\n\n\ndef get_fwhmx():\n    from nipype.interfaces.afni import FWHMx, Info\n\n    fwhm_args = {'combine': True, 'detrend': True}\n    afni_version = Info.version()\n\n    if afni_version and afni_version >= (2017, 2, 3):\n        fwhm_args['args'] = '-ShowMeClassicFWHM'\n\n    fwhm_interface = FWHMx(**fwhm_args)\n    return fwhm_interface\n\n\ndef generate_filename(in_file, dirname=None, suffix='', extension=None):\n    \"\"\"\n    Generate a nipype-like filename.\n\n    >>> str(generate_filename(\"/path/to/input.nii.gz\").relative_to(Path.cwd()))\n    'input.nii.gz'\n\n    >>> str(generate_filename(\n    ...     \"/path/to/input.nii.gz\", dirname=\"/other/path\",\n    ... ))\n    '/other/path/input.nii.gz'\n\n    >>> str(generate_filename(\n    ...     \"/path/to/input.nii.gz\", dirname=\"/other/path\", extension=\"tsv\",\n    ... ))\n    '/other/path/input.tsv'\n\n    >>> str(generate_filename(\n    ...     \"/path/to/input.nii.gz\", dirname=\"/other/path\", extension=\".tsv\",\n    ... ))\n    '/other/path/input.tsv'\n\n    >>> str(generate_filename(\n    ...     \"/path/to/input.nii.gz\", dirname=\"/other/path\", extension=\"\",\n    ... ))\n    '/other/path/input'\n\n    >>> str(generate_filename(\n    ...     \"/path/to/input.nii.gz\", dirname=\"/other/path\", extension=\"\", suffix=\"_mod\",\n    ... ))\n    '/other/path/input_mod'\n\n    >>> str(generate_filename(\n    ...     \"/path/to/input.nii.gz\", dirname=\"/other/path\", extension=\"\", suffix=\"mod\",\n    ... ))\n    '/other/path/input_mod'\n\n    >>> str(generate_filename(\n    ...     \"/path/to/input\", dirname=\"/other/path\", extension=\"tsv\", suffix=\"mod\",\n    ... ))\n    '/other/path/input_mod.tsv'\n\n    \"\"\"\n    from pathlib import Path\n\n    in_file = Path(in_file)\n    in_ext = ''.join(in_file.suffixes)\n\n    dirname = Path.cwd() if dirname is None else Path(dirname)\n\n    if extension is not None:\n        extension = extension if not extension or extension.startswith('.') else f'.{extension}'\n    else:\n        extension = in_ext\n\n    stem = in_file.name[: -len(in_ext)] if in_ext else in_file.name\n\n    if suffix and not suffix.startswith('_'):\n        suffix = f'_{suffix}'\n\n    return dirname / f'{stem}{suffix}{extension}'\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"hatchling>=1.27\", \"hatch-vcs\", \"nipreps-versions\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nauthors = [{name = \"The NiPreps Developers\", email = \"nipreps@gmail.com\"}]\nclassifiers = [\n  \"Development Status :: 5 - Production/Stable\",\n  \"Intended Audience :: Science/Research\",\n  \"Topic :: Scientific/Engineering :: Image Recognition\",\n  \"Topic :: Scientific/Engineering :: Bio-Informatics\",\n  \"Programming Language :: Python :: 3.9\",\n  \"Programming Language :: Python :: 3.10\",\n  \"Programming Language :: Python :: 3.11\",\n  \"Programming Language :: Python :: 3.12\",\n  \"Programming Language :: Python :: 3.13\",\n]\ndependencies = [\n  \"acres\",\n  \"dipy >= 1.10.0\",\n  \"markupsafe ~= 2.0.1\",\n  \"matplotlib\",\n  \"migas >= 0.4.0\",\n  \"mriqc-learn\",\n  \"nibabel >= 3.0.1\",\n  \"nilearn >= 0.5.1\",\n  \"nipype ~= 1.4\",\n  \"nireports ~= 24.0.2\",\n  \"nitransforms ~= 24.0\",\n  \"niworkflows ~=1.10.1\",\n  \"numpy ~=1.20\",\n  \"orjson\",\n  \"pandas\",\n  \"pybids >= 0.15.6\",\n  \"PyYAML\",\n  \"scikit-learn\",\n  \"scipy ~=1.8\",\n  \"statsmodels\",\n  \"templateflow\",\n  \"toml\",\n  \"tomli >= 1.1.0; python_version < '3.11'\",\n  \"torch >= 1.10.2\",\n]\ndescription = \"Automated Quality Control and visual reports for Quality Assessment of structural (T1w, T2w) and functional MRI of the brain.\"\ndynamic = [\"version\"]\nlicense = \"Apache-2.0\"\nlicense-files = [\"LICENSE\"]\nname = \"mriqc\"\nreadme = \"README.rst\"\nrequires-python = \">=3.9\"\n\n[project.urls]\n\"Docker Images\" = \"https://hub.docker.com/r/nipreps/mriqc/tags/\"\nDocumentation = \"https://mriqc.readthedocs.io/\"\nHomepage = \"https://github.com/nipreps/mriqc\"\nManuscript = \"https://doi.org/10.1371/journal.pone.0184661\"\nNiPreps = \"https://www.nipreps.org/\"\n\"Sci Dat Paper\" = \"https://doi.org/10.1038/s41597-019-0035-4\"\n\"Web API\" = \"https://mriqc.nimh.nih.gov/\"\n\n[project.optional-dependencies]\ndoc = [\n  \"nipype\",\n  \"packaging\",\n  \"pydot>=1.2.3\",\n  \"pydotplus\",\n  \"sphinx-argparse\",\n  \"sphinx_rtd_theme\",\n  \"sphinx~=5.0\",\n]\n\ncontainer = [\n  # templateflow extras\n  \"datalad\",\n  \"datalad-osf\",\n]\n\nrodents = [\n  \"nirodents >= 0.2.8\",\n]\n\nnotebook = [\n  \"ipython\",\n  \"jupyter\",\n  \"nipy\",\n]\n\ndev = [\n    \"ruff\",\n]\n\ntest = [\n  \"coverage\",\n  \"mock\",\n  \"pytest\",\n  \"pytest-cov\",\n  \"pytest-env\",\n  \"pytest-xdist\",\n]\n\nother = [\n  \"nitime\",\n  \"scikit-image\",\n  \"seaborn\",\n  \"xvfbwrapper\",\n]\n\n# Aliases\nall = [\"mriqc[doc,container,test,notebook,other,rodents]\"]\ndocs = [\"mriqc[doc]\"]\nothers = [\"mriqc[other]\"]\ntests = [\"mriqc[test]\"]\n\n[project.scripts]\nabide2bids = \"mriqc.bin.abide2bids:main\"\ndfcheck = \"mriqc.bin.dfcheck:main\"\nfs2gif = \"mriqc.bin.fs2gif:main\"\nmriqc = \"mriqc.cli.run:main\"\nmriqc_labeler = \"mriqc.bin.labeler:main\"\nmriqcwebapi_test = \"mriqc.bin.mriqcwebapi_test:main\"\nnib-hash = \"mriqc.bin.nib_hash:main\"\nparticipants = \"mriqc.bin.subject_wrangler:main\"\nsynthstrip = \"mriqc.synthstrip.cli:main\"\n\n#\n# Hatch configurations\n#\n\n[tool.hatch.metadata]\nallow-direct-references = true\n\n[tool.hatch.build.targets.sdist]\nexclude = [\".git_archival.txt\"] # No longer needed in sdist\n\n[tool.hatch.build.targets.wheel]\npackages = [\"mriqc\"]\n# exclude = [\n#     \"mriqc/tests/data\",  # Large test data directory\n# ]\n\n##  The following two sections configure setuptools_scm in the hatch way\n\n[tool.hatch.version]\nraw-options = {version_scheme = \"nipreps-calver\"}\nsource = \"vcs\"\n\n[tool.hatch.build.hooks.vcs]\nversion-file = \"mriqc/_version.py\"\n\n#\n# Developer tool configurations\n#\n\n[tool.pytest.ini_options]\naddopts = [\"-svx\", \"--doctest-modules\", \"-ra\", \"--strict-config\", \"--strict-markers\"]\ndoctest_optionflags = \"ALLOW_UNICODE NORMALIZE_WHITESPACE ELLIPSIS\"\nenv = \"PYTHONHASHSEED=0\"\nfilterwarnings = [\"ignore::DeprecationWarning\"]\njunit_family = \"xunit2\"\nlog_level = \"INFO\"\nminversion = \"7\"\nnorecursedirs = [\".git\"]\nxfail_strict = true\n\n[tool.coverage.run]\nbranch = true\nomit = [\n  '*/tests/*',\n  '*/__init__.py',\n  '*/conftest.py',\n  'mriqc/_version.py',\n]\n\n[tool.coverage.report]\n# Regexes for lines to exclude from consideration\nexclude_lines = [\n  'raise NotImplementedError',\n  'warnings\\.warn',\n]\n\n[tool.codespell]\n# nd - import scipy.ndimage as nd\n# mapp, reson -- Mapp. and Reson. abbreviations in citation\nignore-words-list = 'nd,mapp,reson'\nskip = \"\"\"\n./.git,*.pdf,*.svg,*.min.js,*.ipynb,ORIGINAL_LICENSE,\\\n./docs/source/_static/example_anatreport.html\"\"\"\n\n[tool.ruff]\nexclude = [\".maint/update_authors.py\"]\nline-length = 99\n\n[tool.ruff.lint]\nextend-select = [\n  \"W\",\n  \"I\",\n  \"UP\",\n  \"YTT\",\n  \"S\",\n  \"BLE\",\n  \"B\",\n  \"A\",\n  \"C4\",\n  \"DTZ\",\n  \"T10\",\n  \"EXE\",\n  \"FA\",\n  \"ISC\",\n  \"ICN\",\n  \"PT\",\n  \"Q\",\n]\nignore = [\n  \"F841\",\n  \"S310\",\n  \"S311\", # We are not using random for cryptographic purposes\n  \"S603\",\n]\n\n[tool.ruff.lint.flake8-quotes]\ninline-quotes = \"single\"\n\n[tool.ruff.lint.extend-per-file-ignores]\n\"*/test_*.py\" = [\"S101\"]\n\"docs/notebooks/*.ipynb\" = [\"B018\", \"A001\", \"A002\", \"F401\", \"F821\"]\n\"docs/source/conf.py\" = [\"A001\"]\n\"mriqc/bin/nib_hash.py\" = [\"S324\"]\n\"mriqc/config.py\" = [\"S105\"]\n\"mriqc/conftest.py\" = [\"PT004\"]\n\"mriqc/engine/plugin.py\" = [\"BLE001\"]\n\"mriqc/utils/debug.py\" = [\"A002\", \"T100\"]\n\n[tool.ruff.format]\nquote-style = \"single\"\n"
  }
]