From f8972dac90f75d7e051723b19e6bb826f1cb696e Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 15 Feb 2022 14:21:59 -0600 Subject: [PATCH 001/220] Initial recipe change to include FastSurfer (GPU) support Dockerfile building from a PyTorch base image to facilitate running FastSurfer (GPU). Testing this image for FastSurfer - once built as smriprep:fastsurfer_dev - can be performed with the following command: docker run --gpus all --entrypoint /bin/bash -v /path/to/bids:/data -v /path/to/bids/derivatives/fastsurfer:/output -v /path/to/freesurfer/license:/fs60/license --rm --user `id -u` smriprep:fastsurfer_dev /opt/FastSurfer/run_fastsurfer.sh --fs_license /fs60/license --t1 /data/sourcedata/sub-SUBJECT/ses-SESSION/anat/sub-SUBJECT_ses-SESSION_T1w.nii.gz --sid sub-SUBJECT --sd /output --- Dockerfile | 76 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index d2d16dba1e..3f889f0c56 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,19 @@ # SOFTWARE. # Use Ubuntu 20.04 LTS -FROM ubuntu:focal-20210416 +#FROM ubuntu:focal-20210416 + +# Use pytorch official docker image +FROM pytorch/pytorch:1.2-cuda10.0-cudnn7-runtime + +ENV CUDA_VERSION=10.0.130 \ + CUDA_PKG_VERSION=10-0=10.0.130-1 \ + NCCL_VERSION=2.4.8 \ + CUDNN_VERSION=7.6.5.32 \ + PYTHON_VERSION=3.6 \ + DEBIAN_FRONTEND="noninteractive" \ + LANG="C.UTF-8" \ + LC_ALL="C.UTF-8" # Prepare environment RUN apt-get update && \ @@ -36,15 +48,25 @@ RUN apt-get update && \ curl \ git \ libtool \ + wget \ lsb-release \ pkg-config \ unzip \ + cmake \ + gawk \ + perl-modules \ + tcsh \ + time \ + bzip2 \ + libx11-6 \ + libjpeg-dev \ + bc \ + libgomp1 \ + libglu1-mesa \ + libglu1-mesa-dev \ xvfb && \ - apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + rm -rf /var/lib/apt/lists/* -ENV DEBIAN_FRONTEND="noninteractive" \ - LANG="en_US.UTF-8" \ - LC_ALL="en_US.UTF-8" # Installing freesurfer RUN curl -sSL https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/6.0.1/freesurfer-Linux-centos6_x86_64-stable-pub-v6.0.1.tar.gz \ @@ -68,6 +90,25 @@ RUN curl -sSL https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/6.0.1/frees --exclude='freesurfer/subjects/V1_average' \ --exclude='freesurfer/trctrain' +# Install miniconda and needed python packages (for FastSurferCNN) +#RUN wget -qO ~/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && \ +# chmod +x ~/miniconda.sh && \ +# ~/miniconda.sh -b -p /opt/conda && \ +# rm ~/miniconda.sh && \ +RUN /opt/conda/bin/conda install python-dateutil pyyaml numpy scipy matplotlib h5py scikit-image && \ + /opt/conda/bin/conda install -y -c pytorch cudatoolkit=10.0 "pytorch=1.2.0=py3.6_cuda10.0.130_cudnn7.6.2_0" torchvision=0.4.0 && \ + /opt/conda/bin/conda install -c conda-forge scikit-sparse nibabel=2.5.1 pillow=8.3.2 && \ + /opt/conda/bin/conda clean -ya +ENV PATH /opt/conda/bin:$PATH + +# install LaPy for FastSurfer +RUN python3.6 -m pip install -U git+https://github.com/Deep-MI/LaPy.git#egg=lapy + +# Add FastSurfer (copy application code) to docker image +RUN cd /opt && git clone https://github.com/Deep-MI/FastSurfer.git +ENV FASTSURFER_HOME=/opt/FastSurfer + + # Simulate SetUpFreeSurfer.sh ENV FSL_DIR="/opt/fsl-6.0.5.1" \ OS="Linux" \ @@ -166,12 +207,23 @@ ENV FSLDIR="/opt/fsl-6.0.5.1" \ FSLREMOTECALL="" \ FSLGECUDAQ="cuda.q" \ LD_LIBRARY_PATH="/opt/fsl-6.0.5.1/lib:$LD_LIBRARY_PATH" +# switch back to en-US utf-8 +RUN apt-get update -qq && apt-get install locales && \ + sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \ + locale-gen +ENV LANG="en_US.UTF-8" \ + LC_ALL="en_US.UTF-8" \ + LANGUAGE="en_US:en" +RUN ls /etc/ssl/certs/ -# Convert3D (neurodocker build) -RUN echo "Downloading Convert3D ..." \ - && mkdir -p /opt/convert3d-1.0.0 \ - && curl -fsSL --retry 5 https://sourceforge.net/projects/c3d/files/c3d/1.0.0/c3d-1.0.0-Linux-x86_64.tar.gz/download \ - | tar -xz -C /opt/convert3d-1.0.0 --strip-components 1 \ + +# Convert3D (neurodocker build) - adapted to allow for local copy of Convert3D +# my build machine was giving ca-certificates errors for fetching C3D from sourceforge in build +#RUN echo "Downloading Convert3D ..." \ +# && mkdir -p /opt/convert3d-1.0.0 \ +# && cd /opt/ && curl -fsSL https://sourceforge.net/projects/c3d/files/c3d/1.0.0/c3d-1.0.0-Linux-x86_64.tar.gz \ +COPY c3d-1.0.0-Linux-x86_64.tar.gz /opt/c3d-1.0.0-Linux-x86_64.tar.gz +RUN cd /opt/ && mkdir -p /opt/convert3d-1.0.0 && tar -xzf c3d-1.0.0-Linux-x86_64.tar.gz -C /opt/convert3d-1.0.0 --strip-components 1 \ --exclude "c3d-1.0.0-Linux-x86_64/lib" \ --exclude "c3d-1.0.0-Linux-x86_64/share" \ --exclude "c3d-1.0.0-Linux-x86_64/bin/c3d_gui" @@ -194,7 +246,7 @@ RUN apt-get update -qq \ tcsh \ xfonts-base \ xvfb \ - && apt-get clean \ + && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* \ && curl -sSL --retry 5 -o /tmp/multiarch.deb http://archive.ubuntu.com/ubuntu/pool/main/g/glibc/multiarch-support_2.27-3ubuntu1.2_amd64.deb \ && dpkg -i /tmp/multiarch.deb \ @@ -205,7 +257,7 @@ RUN apt-get update -qq \ && curl -sSL --retry 5 -o /tmp/libpng.deb http://snapshot.debian.org/archive/debian-security/20160113T213056Z/pool/updates/main/libp/libpng/libpng12-0_1.2.49-1%2Bdeb7u2_amd64.deb \ && dpkg -i /tmp/libpng.deb \ && rm /tmp/libpng.deb \ - && apt-get install -f \ + && apt-get install -f -y \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && gsl2_path="$(find / -name 'libgsl.so.19' || printf '')" \ From 1377f4c629542ba8d3ab11f90b9c593ffa29b6cc Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Wed, 20 Apr 2022 15:57:11 -0500 Subject: [PATCH 002/220] Added function to test for FastSurfer files in --fs-subjects-dir Checks FreeSurfer subjects dir for presence of files in mri/ with names indicating processing with FastSurfer, and returns a boolean fastsurfer_bool to indicate that FastSurfer is being used instead of Freesurfer. For development purposes, this also touches files that are expected outputs of Freesurfer, but not produced by default in FastSurfer. (Addresses #278 & https://github.com/Deep-MI/FastSurfer/issues/21) --- smriprep/utils/misc.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/smriprep/utils/misc.py b/smriprep/utils/misc.py index 99d1458866..a0988269af 100644 --- a/smriprep/utils/misc.py +++ b/smriprep/utils/misc.py @@ -95,3 +95,44 @@ def fs_isRunning(subjects_dir, subject_id, mtime_tol=86400, logger=None): if logger: logger.warn(f'Removed "IsRunning*" files found under {subj_dir}') return subjects_dir + +def check_fastsurfer(subjects_dir, subject_id): + """ + Checks FreeSurfer subjects dir for presence of files in mri/ with names indicating processing with FastSurfer, + and returns a boolean fastsurfer_bool to indicate that FastSurfer is being used instead of Freesurfer. + + For development purposes, this also touches files that are expected outputs of Freesurfer, + but not produced by default in FastSurfer. + + Parameters + ---------- + subjects_dir : os.PathLike or None + Existing FreeSurfer subjects directory + subject_id : str + Subject label + + Returns + ------- + fastsurfer_bool : Boolean + subjects_dir : os.PathLike or None + + + """ + from pathlib import Path + + if subjects_dir is None: + return subjects_dir + subj_dir = Path(subjects_dir) / subject_id + if not subj_dir.exists(): + + return subjects_dir + + fastsurferfiles = tuple(subj_dir.glob("mri/*deep*mgz")) + if not fastsurferfiles: + fastsurfer_bool = False + return fastsurfer_bool, subjects_dir + else: + fastsurfer_bool = True + noCCseglabel = Path(subj_dir / 'mri/aseg.auto_noCCseg.label_intensities.txt') + noCCseglabel.touch(exist_ok=False) + return fastsurfer_bool, subjects_dir From 9b6251ff4f1c71f80b80a24a40651cb09a93fd38 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Sat, 23 Apr 2022 17:30:49 -0500 Subject: [PATCH 003/220] Add logger for FastSurfer check Add logger for the check_fastsurfer function to indicate if evidence of FastSurfer surface recon was used to generate the given subjects_dir --- smriprep/utils/misc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/smriprep/utils/misc.py b/smriprep/utils/misc.py index a0988269af..a4d234e202 100644 --- a/smriprep/utils/misc.py +++ b/smriprep/utils/misc.py @@ -96,7 +96,7 @@ def fs_isRunning(subjects_dir, subject_id, mtime_tol=86400, logger=None): logger.warn(f'Removed "IsRunning*" files found under {subj_dir}') return subjects_dir -def check_fastsurfer(subjects_dir, subject_id): +def check_fastsurfer(subjects_dir, subject_id, logger=None): """ Checks FreeSurfer subjects dir for presence of files in mri/ with names indicating processing with FastSurfer, and returns a boolean fastsurfer_bool to indicate that FastSurfer is being used instead of Freesurfer. @@ -133,6 +133,8 @@ def check_fastsurfer(subjects_dir, subject_id): return fastsurfer_bool, subjects_dir else: fastsurfer_bool = True + if logger: + logger.warn(f'Evidence of FastSurfer processing found in {subj_dir}') noCCseglabel = Path(subj_dir / 'mri/aseg.auto_noCCseg.label_intensities.txt') noCCseglabel.touch(exist_ok=False) return fastsurfer_bool, subjects_dir From f301f3ab0d1e037b85625d125db01b95df40f096 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:42:55 -0500 Subject: [PATCH 004/220] Add quick fix check for FastSurfer Adds the check_fastsurfer function call to log FastSurfer outputs detected in subjects_dir and touch mri/aseg.auto_noCCseg.label_intensities.txt to prevent failure in surfaces.py as a temporary fix for https://github.com/nipreps/smriprep/issues/278 --- smriprep/workflows/anatomical.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index f0ae4da2e4..f3ba1b400a 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -47,7 +47,7 @@ from niworkflows.utils.misc import fix_multi_T1w_source_name, add_suffix from niworkflows.anat.ants import init_brain_extraction_wf, init_n4_only_wf from ..utils.bids import get_outputnode_spec -from ..utils.misc import apply_lut as _apply_bids_lut, fs_isRunning as _fs_isRunning +from ..utils.misc import apply_lut as _apply_bids_lut, fs_isRunning as _fs_isRunning, check_fastsurfer as _check_fastsurfer from .norm import init_anat_norm_wf from .outputs import init_anat_reports_wf, init_anat_derivatives_wf from .surfaces import init_surface_recon_wf @@ -514,7 +514,14 @@ def _check_img(img): niu.Function(function=_fs_isRunning), overwrite=True, name="fs_isrunning" ) fs_isrunning.inputs.logger = LOGGER - + + # check for FastSurfer .mgz files and + # touch mri/aseg.auto_noCCseg.label_intensities.txt to prevent failure in surfaces.py (temporary fix) + check_fastsurfer = pe.Node( + niu.Function(function=_check_fastsurfer), overwrite=True, name="check_fastsurfer" + ) + check_fastsurfer.inputs.logger = LOGGER + # 5. Surface reconstruction (--fs-no-reconall not set) surface_recon_wf = init_surface_recon_wf( name="surface_recon_wf", omp_nthreads=omp_nthreads, hires=hires @@ -522,6 +529,9 @@ def _check_img(img): applyrefined = pe.Node(fsl.ApplyMask(), name="applyrefined") # fmt:off workflow.connect([ + (inputnode, check_fastsurfer, [ + ('subjects_dir', 'subjects_dir'), + ('subject_id', 'subject_id')]), (inputnode, fs_isrunning, [ ('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), From f90738a8915527594d386b1c87a4e9e3de29aeec Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:43:37 -0500 Subject: [PATCH 005/220] Removes fastsurfer_bool Removes currently unused fastsurfer_bool variable --- smriprep/utils/misc.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/smriprep/utils/misc.py b/smriprep/utils/misc.py index a4d234e202..e35a15131d 100644 --- a/smriprep/utils/misc.py +++ b/smriprep/utils/misc.py @@ -99,10 +99,7 @@ def fs_isRunning(subjects_dir, subject_id, mtime_tol=86400, logger=None): def check_fastsurfer(subjects_dir, subject_id, logger=None): """ Checks FreeSurfer subjects dir for presence of files in mri/ with names indicating processing with FastSurfer, - and returns a boolean fastsurfer_bool to indicate that FastSurfer is being used instead of Freesurfer. - - For development purposes, this also touches files that are expected outputs of Freesurfer, - but not produced by default in FastSurfer. + this also touches files that are expected outputs of Freesurfer, but not produced by default in FastSurfer. Parameters ---------- @@ -113,7 +110,6 @@ def check_fastsurfer(subjects_dir, subject_id, logger=None): Returns ------- - fastsurfer_bool : Boolean subjects_dir : os.PathLike or None @@ -124,17 +120,16 @@ def check_fastsurfer(subjects_dir, subject_id, logger=None): return subjects_dir subj_dir = Path(subjects_dir) / subject_id if not subj_dir.exists(): - return subjects_dir fastsurferfiles = tuple(subj_dir.glob("mri/*deep*mgz")) if not fastsurferfiles: fastsurfer_bool = False - return fastsurfer_bool, subjects_dir + return subjects_dir else: fastsurfer_bool = True if logger: logger.warn(f'Evidence of FastSurfer processing found in {subj_dir}') noCCseglabel = Path(subj_dir / 'mri/aseg.auto_noCCseg.label_intensities.txt') noCCseglabel.touch(exist_ok=False) - return fastsurfer_bool, subjects_dir + return subjects_dir From 42e6456d753c8cf0561ad5d4d93a2c56777c8d8d Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Sat, 23 Apr 2022 19:12:22 -0500 Subject: [PATCH 006/220] Add FastSurfer argument to Adds FastSurfer argument to support FastSurfer as a surface processing option (https://github.com/nipreps/smriprep/issues/278) --- smriprep/cli/run.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/smriprep/cli/run.py b/smriprep/cli/run.py index 4a588283f8..a03cb1f489 100644 --- a/smriprep/cli/run.py +++ b/smriprep/cli/run.py @@ -222,6 +222,14 @@ def get_parser(): dest="hires", help="disable sub-millimeter (hires) reconstruction", ) + + g_surfs_xor.add_argument( + "--fastsurfer-recon", + action="store_true", + dest="run_fastsurfer", + help="enable FastSurfer surface preprocessing.", + ) + g_surfs_xor = g_surfs.add_mutually_exclusive_group() g_surfs_xor.add_argument( @@ -394,6 +402,17 @@ def _warn_redirect(message, category, filename, lineno, file=None, line=None): from templateflow import api from niworkflows.utils.misc import _copy_any + dseg_tsv = str(api.get("fsaverage", suffix="dseg", extension=[".tsv"])) + _copy_any( + dseg_tsv, str(Path(output_dir) / "smriprep" / "desc-aseg_dseg.tsv") + ) + _copy_any( + dseg_tsv, str(Path(output_dir) / "smriprep" / "desc-aparcaseg_dseg.tsv") + ) + elif opts.run_fastsurfer: + from templateflow import api + from niworkflows.utils.misc import _copy_any + dseg_tsv = str(api.get("fsaverage", suffix="dseg", extension=[".tsv"])) _copy_any( dseg_tsv, str(Path(output_dir) / "smriprep" / "desc-aseg_dseg.tsv") @@ -584,6 +603,7 @@ def build_workflow(opts, retval): freesurfer=opts.run_reconall, fs_subjects_dir=opts.fs_subjects_dir, hires=opts.hires, + fastsurfer=opts.run_fastsurfer, layout=layout, longitudinal=opts.longitudinal, low_mem=opts.low_mem, From 7502350e47f5a61378390979cf35cc9843010b7c Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Sun, 24 Apr 2022 16:58:43 -0500 Subject: [PATCH 007/220] Add FastSurfer citations Add citations for FastSurfer: 1mm isotropic original paper = fastsurfer, 2022 hires version (NOT YET IMPLEMENTED) = fastsurfer_hires --- smriprep/data/boilerplate.bib | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/smriprep/data/boilerplate.bib b/smriprep/data/boilerplate.bib index 76e67fa503..1bf8286e0c 100644 --- a/smriprep/data/boilerplate.bib +++ b/smriprep/data/boilerplate.bib @@ -62,6 +62,34 @@ @article{fs_reconall year = 1999 } +@article{fastsurfer, +title = {FastSurfer - A fast and accurate deep learning based neuroimaging pipeline}, +journal = {NeuroImage}, +volume = {219}, +pages = {117012}, +year = {2020}, +issn = {1053-8119}, +doi = {https://doi.org/10.1016/j.neuroimage.2020.117012}, +url = {https://www.sciencedirect.com/science/article/pii/S1053811920304985}, +author = {Leonie Henschel and Sailesh Conjeti and Santiago Estrada and Kersten Diers and Bruce Fischl and Martin Reuter}, +keywords = {Freesurfer, Computational neuroimaging, Deep learning, Structural MRI, Artificial intelligence}, +abstract = {Traditional neuroimage analysis pipelines involve computationally intensive, time-consuming optimization steps, and thus, do not scale well to large cohort studies with thousands or tens of thousands of individuals. In this work we propose a fast and accurate deep learning based neuroimaging pipeline for the automated processing of structural human brain MRI scans, replicating FreeSurfer’s anatomical segmentation including surface reconstruction and cortical parcellation. To this end, we introduce an advanced deep learning architecture capable of whole-brain segmentation into 95 classes. The network architecture incorporates local and global competition via competitive dense blocks and competitive skip pathways, as well as multi-slice information aggregation that specifically tailor network performance towards accurate segmentation of both cortical and subcortical structures. Further, we perform fast cortical surface reconstruction and thickness analysis by introducing a spectral spherical embedding and by directly mapping the cortical labels from the image to the surface. This approach provides a full FreeSurfer alternative for volumetric analysis (in under 1 ​min) and surface-based thickness analysis (within only around 1 ​h runtime). For sustainability of this approach we perform extensive validation: we assert high segmentation accuracy on several unseen datasets, measure generalizability and demonstrate increased test-retest reliability, and high sensitivity to group differences in dementia.} +} + +@article{fastsufrer_hires, +title = {FastSurferVINN: Building resolution-independence into deep learning segmentation methods—A solution for HighRes brain MRI}, +journal = {NeuroImage}, +volume = {251}, +pages = {118933}, +year = {2022}, +issn = {1053-8119}, +doi = {https://doi.org/10.1016/j.neuroimage.2022.118933}, +url = {https://www.sciencedirect.com/science/article/pii/S1053811922000623}, +author = {Leonie Henschel and David Kügler and Martin Reuter}, +keywords = {Computational neuroimaging, Deep learning, Structural MRI, Artificial intelligence, High-resolution}, +abstract = {Leading neuroimaging studies have pushed 3T MRI acquisition resolutions below 1.0 mm for improved structure definition and morphometry. Yet, only few, time-intensive automated image analysis pipelines have been validated for high-resolution (HiRes) settings. Efficient deep learning approaches, on the other hand, rarely support more than one fixed resolution (usually 1.0 mm). Furthermore, the lack of a standard submillimeter resolution as well as limited availability of diverse HiRes data with sufficient coverage of scanner, age, diseases, or genetic variance poses additional, unsolved challenges for training HiRes networks. Incorporating resolution-independence into deep learning-based segmentation, i.e., the ability to segment images at their native resolution across a range of different voxel sizes, promises to overcome these challenges, yet no such approach currently exists. We now fill this gap by introducing a Voxel-size Independent Neural Network (VINN) for resolution-independent segmentation tasks and present FastSurferVINN, which (i) establishes and implements resolution-independence for deep learning as the first method simultaneously supporting 0.7–1.0 mm whole brain segmentation, (ii) significantly outperforms state-of-the-art methods across resolutions, and (iii) mitigates the data imbalance problem present in HiRes datasets. Overall, internal resolution-independence mutually benefits both HiRes and 1.0 mm MRI segmentation. With our rigorously validated FastSurferVINN we distribute a rapid tool for morphometric neuroimage analysis. The VINN architecture, furthermore, represents an efficient resolution-independent segmentation method for wider application.} +} + @article{mindboggle, author = {Klein, Arno and Ghosh, Satrajit S. and Bao, Forrest S. and Giard, Joachim and Häme, Yrjö and Stavsky, Eliezer and Lee, Noah and Rossa, Brian and Reuter, Martin and Neto, Elias Chaibub and Keshavan, Anisha}, doi = {10.1371/journal.pcbi.1005350}, From 4cf37e55bdb667f82dc6e04c19eb92dc8192d2a6 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Sun, 24 Apr 2022 17:08:30 -0500 Subject: [PATCH 008/220] Add FastSurfer --- docs/installation.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/installation.rst b/docs/installation.rst index a4c6d204b1..792763546d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -150,3 +150,4 @@ the ``smriprep`` package: - `C3D `_ (version 1.0.0) - FreeSurfer_ (version 6.0.1) - `bids-validator `_ (version 1.1.0) +- FastSurfer_ (version 1.0.1) From c8d0493dd8748aa6dcb7eb4270d5a384f1fc7fe5 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Mon, 25 Apr 2022 21:31:52 -0500 Subject: [PATCH 009/220] Add FastSurfer python wrapper Adds python wrapper for FastSurfer (https://github.com/nipreps/smriprep/issues/278) --- smriprep/interfaces/fastsurfer.py | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 smriprep/interfaces/fastsurfer.py diff --git a/smriprep/interfaces/fastsurfer.py b/smriprep/interfaces/fastsurfer.py new file mode 100644 index 0000000000..dda5c36817 --- /dev/null +++ b/smriprep/interfaces/fastsurfer.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +"""The FastSufer module provides basic functions for running FastSurfer CNN +and surface processing. + +Examples +-------- +See the docstrings for the individual classes for 'working' examples. + +""" +from ast import BoolOp +from genericpath import exists +import os +from xmlrpc.client import Boolean + +from nipype.interfaces.base import ( + CommandLine, + Directory, + CommandLineInputSpec, + isdefined, + TraitedSpec, + File, + PackageInfo, +) +from nipype.interfaces.base.traits_extension import traits +__docformat__ = "restructuredtext" + +class FastSInputSpec(CommandLineInputSpec): + """ + Required arguments + ------------------ + --sd: Output directory $SUBJECTS_DIR (equivalent to FreeSurfer setup --> $SUBJECTS_DIR/sid/mri; $SUBJECTS_DIR/sid/surf ... will be created). + --sid: Subject ID for directory inside $SUBJECTS_DIR to be created ($SUBJECTS_DIR/sid/...) + --t1: T1 full head input (not bias corrected, global path). The network was trained with conformed images (UCHAR, 256x256x256, 1 mm voxels and standard slice orientation). These specifications are checked in the eval.py script and the image is automatically conformed if it does not comply. + --fs_license: Path to FreeSurfer license key file. Register (for free) at https://surfer.nmr.mgh.harvard.edu/registration.html to obtain it if you do not have FreeSurfer installed so far. Strictly necessary if you use Docker, optional for local install (your local FreeSurfer license will automatically be used) + + Optional arguments + ------------------ + + Network specific arguments: + --seg: Global path with filename of segmentation (where and under which name to store it). Default location: $SUBJECTS_DIR/$sid/mri/aparc.DKTatlas+aseg.deep.mgz + --weights_sag: Pretrained weights of sagittal network. Default: ../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl + --weights_ax: Pretrained weights of axial network. Default: ../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl + --weights_cor: Pretrained weights of coronal network. Default: ../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl + --seg_log: Name and location for the log-file for the segmentation (FastSurferCNN). Default: $SUBJECTS_DIR/$sid/scripts/deep-seg.log + --clean_seg: Flag to clean up FastSurferCNN segmentation + --run_viewagg_on: Define where the view aggregation should be run on. By default, the program checks if you have enough memory to run the view aggregation on the gpu. The total memory is considered for this decision. If this fails, or you actively overwrote the check with setting "--run_viewagg_on cpu", view agg is run on the cpu. Equivalently, if you define "--run_viewagg_on gpu", view agg will be run on the gpu (no memory check will be done). + --no_cuda: Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU) + --batch: Batch size for inference. Default: 16. Lower this to reduce memory requirement + --order: Order of interpolation for mri_convert T1 before segmentation (0=nearest, 1=linear(default), 2=quadratic, 3=cubic) + + Surface pipeline arguments: + --fstess: Use mri_tesselate instead of marching cube (default) for surface creation + --fsqsphere: Use FreeSurfer default instead of novel spectral spherical projection for qsphere + --fsaparc: Use FS aparc segmentations in addition to DL prediction (slower in this case and usually the mapped ones from the DL prediction are fine) + --surfreg: Create Surface-Atlas (sphere.reg) registration with FreeSurfer (for cross-subject correspondence or other mappings) + --parallel: Run both hemispheres in parallel + --threads: Set openMP and ITK threads to + + Other: + --py: which python version to use. Default: python3.6 + --seg_only: only run FastSurferCNN (generate segmentation, do not run the surface pipeline) + --surf_only: only run the surface pipeline recon_surf. The segmentation created by FastSurferCNN must already exist in this case. + """ + sd = Directory(exists=True, argstr="--sd %s", mandatory=True, desc="Subjects directory") + sid = traits.String(exists=True, argstr="--sid %s", mandatory=True, desc="Subject ID") + t1 = File(exists=True, mandatory=True, argstr="--t1 %s", desc="T1 full head input (not bias corrected, global path)") + fs_license = File(exists=True, mandatory=True, argstr="--fs_license %s", desc="Path to FreeSurfer license key file. Register (for free) at https://surfer.nmr.mgh.harvard.edu/registration.html to obtain it if you do not have FreeSurfer installed so far.") + seg = File(exists=True, mandatory=False, argstr="--seg %s", desc="Global path with filename of segmentation (where and under which name to store it)") + weights_sag = File(exists=True, mandatory=False, default="../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", usedefault=False, argstr="--weights_sag %s", desc="Pretrained weights of sagittal network") + weights_ax = File(exists=True, mandatory=False, default="../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", usedefault=False, argstr="--weights_ax %s", desc="Pretrained weights of axial network",) + weights_cor = File(exists=True, mandatory=False, default="../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", usedefault=False, argstr="--weights_cor %s", desc="Pretrained weights of coronal network") + seg_log = File(exists=True, mandatory=False, argstr="--seg_log %s", desc="Name and location for the log-file for the segmentation (FastSurferCNN).") + clean_seg = traits.Bool(False,mandatory=False, usedefault=False, argstr="--clean_seg", desc="Flag to clean up FastSurferCNN segmentation") + run_viewagg_on = File(exists=True, mandatory=False, argstr="--run_viewagg_on %s", desc="Define where the view aggregation should be run on. ") + no_cuda = traits.Bool(False,mandatory=False, usedefault=False, argstr="--no_cuda", desc="Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU)") + batch = traits.Int(16, usedefault=True, mandatory=False, argstr="--batch %d", desc="Batch size for inference. default=16. Lower this to reduce memory requirement") + order = traits.Int(1, mandatory=False, argstr = "--order %d", usedefault=True, desc="Order of interpolation for mri_convert T1 before segmentation (0=nearest, 1=linear(default), 2=quadratic, 3=cubic)") + fstess = traits.Bool(False, usedefault=False, mandatory=False, argstr="--fstess", desc="Use mri_tesselate instead of marching cube (default) for surface creation") + fsqsphere = traits.Bool(False, usedefault=False, mandatory=False, argstr="--fsqsphere", desc="Use FreeSurfer default instead of novel spectral spherical projection for qsphere") + fsaparc = traits.Bool(False, usedefault=False, mandatory=False, argstr="--fsaparc", desc="Use FS aparc segmentations in addition to DL prediction (slower in this case and usually the mapped ones from the DL prediction are fine)") + surfreg = traits.Bool(True, usedefault=True, mandatory=False, argstr="--surfreg", desc="Create Surface-Atlas (sphere.reg) registration with FreeSurfer (for cross-subject correspondence or other mappings)") + parallel = traits.Bool(True, usedefault=True, mandatory=False, argstr="--parallel", desc="Run both hemispheres in parallel") + threads = traits.Int(4, usedefault=True, mandatory=False, argstr="--threads %d", desc="Set openMP and ITK threads to") + py = traits.String("python3.6", usedefault=True, mandatory=False, argstr="--py %s", desc="which python version to use. default=python3.6") + seg_only = traits.Bool(False, usedefault=False, mandatory=False, argstr="--seg_only", desc="only run FastSurferCNN (generate segmentation, do not run the surface pipeline)") + surf_only = traits.Bool(False, usedefault=False, mandatory=False, argstr="--surf_only", desc="only run the surface pipeline recon_surf. The segmentation created by FastSurferCNN must already exist in this case.") + +class FastSTraitedOutputSpec(TraitedSpec): + """ + Outputs directory within the FastSurfer subjects_dir/subject_id/ with structure equivalent to Freesurfer + """ + outputs = Directory(exists=True, desc="FastSurfer CNN (or VINN when added) + Surface Pipeline equivalent of the Freesurfer recon-all outputs") + +class FastSCommand(CommandLine): + input_spec = FastSInputSpec + output_spec = FastSTraitedOutputSpec + _cmd = '/opt/FastSurfer/run_fastsurfer.sh' + + def _list_outputs(self): + outputs = self.output_spec().get() + return outputs + \ No newline at end of file From 3169bc7d2b21732ba7c1d55697a173f9058c2b9d Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Mon, 25 Apr 2022 21:43:45 -0500 Subject: [PATCH 010/220] Update .zenodo.json Adds affiliation to contributors for work regarding https://github.com/nipreps/smriprep/issues/278 --- .zenodo.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.zenodo.json b/.zenodo.json index 391b6dad6d..9061e4c9b8 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -46,6 +46,11 @@ "name": "Ghosh, Satrajit", "orcid": "0000-0002-5312-6729", "type": "Researcher" + }, + + "affiliation": "Beckman Institute for Advanced Science & Technology, University of Illinois at Urbana-Champaign, IL, USA", + "name": "Camacho, Paul B.", + "orcid": "0000-0001-9048-7307" } ], "keywords": [ From d5969558213611e20a893d6c3dc3111d88bcfe44 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Mon, 25 Apr 2022 21:44:15 -0500 Subject: [PATCH 011/220] Fix typo Fix typo --- .zenodo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zenodo.json b/.zenodo.json index 9061e4c9b8..1a9312312a 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -47,7 +47,7 @@ "orcid": "0000-0002-5312-6729", "type": "Researcher" }, - + { "affiliation": "Beckman Institute for Advanced Science & Technology, University of Illinois at Urbana-Champaign, IL, USA", "name": "Camacho, Paul B.", "orcid": "0000-0001-9048-7307" From 8feb1725b195fd81ac2dbd68915942ce2e5dc51d Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 16:48:26 -0500 Subject: [PATCH 012/220] Removes fastsurfer_bool for Flake8 check --- smriprep/utils/misc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/smriprep/utils/misc.py b/smriprep/utils/misc.py index e35a15131d..5418b47f81 100644 --- a/smriprep/utils/misc.py +++ b/smriprep/utils/misc.py @@ -124,10 +124,8 @@ def check_fastsurfer(subjects_dir, subject_id, logger=None): fastsurferfiles = tuple(subj_dir.glob("mri/*deep*mgz")) if not fastsurferfiles: - fastsurfer_bool = False return subjects_dir else: - fastsurfer_bool = True if logger: logger.warn(f'Evidence of FastSurfer processing found in {subj_dir}') noCCseglabel = Path(subj_dir / 'mri/aseg.auto_noCCseg.label_intensities.txt') From 94f962ab4020295bcb7f96572f0b269f8c678cd4 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 16:56:53 -0500 Subject: [PATCH 013/220] Update misc.py --- smriprep/utils/misc.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/smriprep/utils/misc.py b/smriprep/utils/misc.py index 5418b47f81..14a0cac985 100644 --- a/smriprep/utils/misc.py +++ b/smriprep/utils/misc.py @@ -112,16 +112,15 @@ def check_fastsurfer(subjects_dir, subject_id, logger=None): ------- subjects_dir : os.PathLike or None - """ from pathlib import Path - + if subjects_dir is None: return subjects_dir subj_dir = Path(subjects_dir) / subject_id if not subj_dir.exists(): return subjects_dir - + fastsurferfiles = tuple(subj_dir.glob("mri/*deep*mgz")) if not fastsurferfiles: return subjects_dir From 94d952a317c70ce3202203ca4f15c543bcb67e17 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 16:57:39 -0500 Subject: [PATCH 014/220] Update fastsurfer.py --- smriprep/interfaces/fastsurfer.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/smriprep/interfaces/fastsurfer.py b/smriprep/interfaces/fastsurfer.py index dda5c36817..42aba765aa 100644 --- a/smriprep/interfaces/fastsurfer.py +++ b/smriprep/interfaces/fastsurfer.py @@ -37,7 +37,6 @@ class FastSInputSpec(CommandLineInputSpec): Optional arguments ------------------ - Network specific arguments: --seg: Global path with filename of segmentation (where and under which name to store it). Default location: $SUBJECTS_DIR/$sid/mri/aparc.DKTatlas+aseg.deep.mgz --weights_sag: Pretrained weights of sagittal network. Default: ../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl @@ -101,4 +100,4 @@ class FastSCommand(CommandLine): def _list_outputs(self): outputs = self.output_spec().get() return outputs - \ No newline at end of file + From 7f9fe47584e0f6443a966af4aeb7ef7b8a234aaf Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 16:59:00 -0500 Subject: [PATCH 015/220] Update anatomical.py --- smriprep/workflows/anatomical.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index f3ba1b400a..1227dc6131 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -516,7 +516,8 @@ def _check_img(img): fs_isrunning.inputs.logger = LOGGER # check for FastSurfer .mgz files and - # touch mri/aseg.auto_noCCseg.label_intensities.txt to prevent failure in surfaces.py (temporary fix) + # touch mri/aseg.auto_noCCseg.label_intensities.txt + # to prevent failure in surfaces.py (temporary fix) check_fastsurfer = pe.Node( niu.Function(function=_check_fastsurfer), overwrite=True, name="check_fastsurfer" ) From 55ea6b0ed25b49fd67f7b00200adccb08c0edfc5 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:00:04 -0500 Subject: [PATCH 016/220] Update .zenodo.json Add researcher type --- .zenodo.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.zenodo.json b/.zenodo.json index 1a9312312a..2d106a019d 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -50,7 +50,8 @@ { "affiliation": "Beckman Institute for Advanced Science & Technology, University of Illinois at Urbana-Champaign, IL, USA", "name": "Camacho, Paul B.", - "orcid": "0000-0001-9048-7307" + "orcid": "0000-0001-9048-7307", + "type": "Researcher" } ], "keywords": [ From 4c6828afa294707e3a7de5b2939e8df447b03555 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:01:40 -0500 Subject: [PATCH 017/220] Update run.py Address PEP8 blankspaces flag --- smriprep/cli/run.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/smriprep/cli/run.py b/smriprep/cli/run.py index a03cb1f489..223fdc071d 100644 --- a/smriprep/cli/run.py +++ b/smriprep/cli/run.py @@ -222,14 +222,12 @@ def get_parser(): dest="hires", help="disable sub-millimeter (hires) reconstruction", ) - g_surfs_xor.add_argument( "--fastsurfer-recon", action="store_true", dest="run_fastsurfer", help="enable FastSurfer surface preprocessing.", ) - g_surfs_xor = g_surfs.add_mutually_exclusive_group() g_surfs_xor.add_argument( From c77ed1de6bed9ee134f78c0ae390503e42ec833b Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:03:50 -0500 Subject: [PATCH 018/220] Update misc.py Address PEP8 --- smriprep/utils/misc.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/smriprep/utils/misc.py b/smriprep/utils/misc.py index 14a0cac985..2b16fb57fa 100644 --- a/smriprep/utils/misc.py +++ b/smriprep/utils/misc.py @@ -96,10 +96,12 @@ def fs_isRunning(subjects_dir, subject_id, mtime_tol=86400, logger=None): logger.warn(f'Removed "IsRunning*" files found under {subj_dir}') return subjects_dir + def check_fastsurfer(subjects_dir, subject_id, logger=None): """ - Checks FreeSurfer subjects dir for presence of files in mri/ with names indicating processing with FastSurfer, - this also touches files that are expected outputs of Freesurfer, but not produced by default in FastSurfer. + Checks FreeSurfer subjects dir for presence of files in mri/ with names \ + indicating processing with FastSurfer, this also touches files that are \ + expected outputs of Freesurfer, but not produced by default in FastSurfer. Parameters ---------- @@ -114,13 +116,13 @@ def check_fastsurfer(subjects_dir, subject_id, logger=None): """ from pathlib import Path - + if subjects_dir is None: return subjects_dir subj_dir = Path(subjects_dir) / subject_id if not subj_dir.exists(): return subjects_dir - + fastsurferfiles = tuple(subj_dir.glob("mri/*deep*mgz")) if not fastsurferfiles: return subjects_dir From 595b0010ee8cdd9d9572fe6c12e132f030e44424 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:06:13 -0500 Subject: [PATCH 019/220] Address PEP8 Address PEP8 --- smriprep/workflows/anatomical.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index 1227dc6131..b7b35ac3d9 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -47,7 +47,11 @@ from niworkflows.utils.misc import fix_multi_T1w_source_name, add_suffix from niworkflows.anat.ants import init_brain_extraction_wf, init_n4_only_wf from ..utils.bids import get_outputnode_spec -from ..utils.misc import apply_lut as _apply_bids_lut, fs_isRunning as _fs_isRunning, check_fastsurfer as _check_fastsurfer +from ..utils.misc import ( + apply_lut as _apply_bids_lut, + fs_isRunning as _fs_isRunning, + check_fastsurfer as _check_fastsurfer +) from .norm import init_anat_norm_wf from .outputs import init_anat_reports_wf, init_anat_derivatives_wf from .surfaces import init_surface_recon_wf @@ -514,15 +518,15 @@ def _check_img(img): niu.Function(function=_fs_isRunning), overwrite=True, name="fs_isrunning" ) fs_isrunning.inputs.logger = LOGGER - - # check for FastSurfer .mgz files and + + # check for FastSurfer .mgz files and # touch mri/aseg.auto_noCCseg.label_intensities.txt # to prevent failure in surfaces.py (temporary fix) check_fastsurfer = pe.Node( niu.Function(function=_check_fastsurfer), overwrite=True, name="check_fastsurfer" ) check_fastsurfer.inputs.logger = LOGGER - + # 5. Surface reconstruction (--fs-no-reconall not set) surface_recon_wf = init_surface_recon_wf( name="surface_recon_wf", omp_nthreads=omp_nthreads, hires=hires From 86edc276eed14a80a522f64c6e07a21e257c3a6f Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:24:26 -0500 Subject: [PATCH 020/220] Update formatting for PEP8 compliance --- smriprep/interfaces/fastsurfer.py | 254 ++++++++++++++++++++++++------ 1 file changed, 210 insertions(+), 44 deletions(-) diff --git a/smriprep/interfaces/fastsurfer.py b/smriprep/interfaces/fastsurfer.py index 42aba765aa..2c9267c937 100644 --- a/smriprep/interfaces/fastsurfer.py +++ b/smriprep/interfaces/fastsurfer.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: -"""The FastSufer module provides basic functions for running FastSurfer CNN -and surface processing. +"""The FastSufer module provides basic functions +for running FastSurfer CNN and surface processing. Examples -------- @@ -26,71 +26,238 @@ from nipype.interfaces.base.traits_extension import traits __docformat__ = "restructuredtext" + class FastSInputSpec(CommandLineInputSpec): """ Required arguments ------------------ - --sd: Output directory $SUBJECTS_DIR (equivalent to FreeSurfer setup --> $SUBJECTS_DIR/sid/mri; $SUBJECTS_DIR/sid/surf ... will be created). - --sid: Subject ID for directory inside $SUBJECTS_DIR to be created ($SUBJECTS_DIR/sid/...) - --t1: T1 full head input (not bias corrected, global path). The network was trained with conformed images (UCHAR, 256x256x256, 1 mm voxels and standard slice orientation). These specifications are checked in the eval.py script and the image is automatically conformed if it does not comply. - --fs_license: Path to FreeSurfer license key file. Register (for free) at https://surfer.nmr.mgh.harvard.edu/registration.html to obtain it if you do not have FreeSurfer installed so far. Strictly necessary if you use Docker, optional for local install (your local FreeSurfer license will automatically be used) + --sd: Output directory $SUBJECTS_DIR + --sid: Subject ID for directory inside $SUBJECTS_DIR to be created + --t1: T1 full head input (not bias corrected, global path). \ + The network was trained with conformed images (UCHAR, 256x256x256, \ + 1 mm voxels and standard slice orientation). \ + These specifications are checked in the eval.py script and the image \ + is automatically conformed if it does not comply. + --fs_license: Path to FreeSurfer license key file. \ + Register at https://surfer.nmr.mgh.harvard.edu/registration.html \ + to obtain it if you do not have FreeSurfer installed so far. Optional arguments ------------------ Network specific arguments: - --seg: Global path with filename of segmentation (where and under which name to store it). Default location: $SUBJECTS_DIR/$sid/mri/aparc.DKTatlas+aseg.deep.mgz - --weights_sag: Pretrained weights of sagittal network. Default: ../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl - --weights_ax: Pretrained weights of axial network. Default: ../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl - --weights_cor: Pretrained weights of coronal network. Default: ../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl - --seg_log: Name and location for the log-file for the segmentation (FastSurferCNN). Default: $SUBJECTS_DIR/$sid/scripts/deep-seg.log + --seg: Global path with filename of segmentation \ + (where and under which name to store it). \ + Default location: \ + $SUBJECTS_DIR/$sid/mri/aparc.DKTatlas+aseg.deep.mgz + --weights_sag: Pretrained weights of sagittal network. \ + Default: \ + ../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl + --weights_ax: Pretrained weights of axial network. \ + Default: \ + ../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl + --weights_cor: Pretrained weights of coronal network. \ + Default: ../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl + --seg_log: Name and location for the log-file for the segmentation (FastSurferCNN). \ + Default: $SUBJECTS_DIR/$sid/scripts/deep-seg.log --clean_seg: Flag to clean up FastSurferCNN segmentation - --run_viewagg_on: Define where the view aggregation should be run on. By default, the program checks if you have enough memory to run the view aggregation on the gpu. The total memory is considered for this decision. If this fails, or you actively overwrote the check with setting "--run_viewagg_on cpu", view agg is run on the cpu. Equivalently, if you define "--run_viewagg_on gpu", view agg will be run on the gpu (no memory check will be done). + --run_viewagg_on: Define where the view aggregation should be run on. \ + By default, the program checks if you have enough memory to run \ + the view aggregation on the gpu. The total memory is considered for this decision. \ + If this fails, or you actively overwrote the check with setting \ + "--run_viewagg_on cpu", view agg is run on the cpu. \ + Equivalently, if you define "--run_viewagg_on gpu", view agg will be run on the gpu \ + (no memory check will be done). --no_cuda: Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU) --batch: Batch size for inference. Default: 16. Lower this to reduce memory requirement - --order: Order of interpolation for mri_convert T1 before segmentation (0=nearest, 1=linear(default), 2=quadratic, 3=cubic) + --order: Order of interpolation for mri_convert T1 before segmentation \ + (0=nearest, 1=linear(default), 2=quadratic, 3=cubic) Surface pipeline arguments: --fstess: Use mri_tesselate instead of marching cube (default) for surface creation - --fsqsphere: Use FreeSurfer default instead of novel spectral spherical projection for qsphere - --fsaparc: Use FS aparc segmentations in addition to DL prediction (slower in this case and usually the mapped ones from the DL prediction are fine) - --surfreg: Create Surface-Atlas (sphere.reg) registration with FreeSurfer (for cross-subject correspondence or other mappings) + --fsqsphere: Use FreeSurfer default instead of \ + novel spectral spherical projection for qsphere + --fsaparc: Use FS aparc segmentations in addition to DL prediction \ + (slower in this case and usually the mapped ones from the DL prediction are fine) + --surfreg: Create Surface-Atlas (sphere.reg) registration with FreeSurfer \ + (for cross-subject correspondence or other mappings) --parallel: Run both hemispheres in parallel --threads: Set openMP and ITK threads to Other: --py: which python version to use. Default: python3.6 - --seg_only: only run FastSurferCNN (generate segmentation, do not run the surface pipeline) - --surf_only: only run the surface pipeline recon_surf. The segmentation created by FastSurferCNN must already exist in this case. + --seg_only: only run FastSurferCNN + (generate segmentation, do not run the surface pipeline) + --surf_only: only run the surface pipeline recon_surf. \ + The segmentation created by FastSurferCNN must already exist in this case. """ - sd = Directory(exists=True, argstr="--sd %s", mandatory=True, desc="Subjects directory") - sid = traits.String(exists=True, argstr="--sid %s", mandatory=True, desc="Subject ID") - t1 = File(exists=True, mandatory=True, argstr="--t1 %s", desc="T1 full head input (not bias corrected, global path)") - fs_license = File(exists=True, mandatory=True, argstr="--fs_license %s", desc="Path to FreeSurfer license key file. Register (for free) at https://surfer.nmr.mgh.harvard.edu/registration.html to obtain it if you do not have FreeSurfer installed so far.") - seg = File(exists=True, mandatory=False, argstr="--seg %s", desc="Global path with filename of segmentation (where and under which name to store it)") - weights_sag = File(exists=True, mandatory=False, default="../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", usedefault=False, argstr="--weights_sag %s", desc="Pretrained weights of sagittal network") - weights_ax = File(exists=True, mandatory=False, default="../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", usedefault=False, argstr="--weights_ax %s", desc="Pretrained weights of axial network",) - weights_cor = File(exists=True, mandatory=False, default="../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", usedefault=False, argstr="--weights_cor %s", desc="Pretrained weights of coronal network") - seg_log = File(exists=True, mandatory=False, argstr="--seg_log %s", desc="Name and location for the log-file for the segmentation (FastSurferCNN).") - clean_seg = traits.Bool(False,mandatory=False, usedefault=False, argstr="--clean_seg", desc="Flag to clean up FastSurferCNN segmentation") - run_viewagg_on = File(exists=True, mandatory=False, argstr="--run_viewagg_on %s", desc="Define where the view aggregation should be run on. ") - no_cuda = traits.Bool(False,mandatory=False, usedefault=False, argstr="--no_cuda", desc="Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU)") - batch = traits.Int(16, usedefault=True, mandatory=False, argstr="--batch %d", desc="Batch size for inference. default=16. Lower this to reduce memory requirement") - order = traits.Int(1, mandatory=False, argstr = "--order %d", usedefault=True, desc="Order of interpolation for mri_convert T1 before segmentation (0=nearest, 1=linear(default), 2=quadratic, 3=cubic)") - fstess = traits.Bool(False, usedefault=False, mandatory=False, argstr="--fstess", desc="Use mri_tesselate instead of marching cube (default) for surface creation") - fsqsphere = traits.Bool(False, usedefault=False, mandatory=False, argstr="--fsqsphere", desc="Use FreeSurfer default instead of novel spectral spherical projection for qsphere") - fsaparc = traits.Bool(False, usedefault=False, mandatory=False, argstr="--fsaparc", desc="Use FS aparc segmentations in addition to DL prediction (slower in this case and usually the mapped ones from the DL prediction are fine)") - surfreg = traits.Bool(True, usedefault=True, mandatory=False, argstr="--surfreg", desc="Create Surface-Atlas (sphere.reg) registration with FreeSurfer (for cross-subject correspondence or other mappings)") - parallel = traits.Bool(True, usedefault=True, mandatory=False, argstr="--parallel", desc="Run both hemispheres in parallel") - threads = traits.Int(4, usedefault=True, mandatory=False, argstr="--threads %d", desc="Set openMP and ITK threads to") - py = traits.String("python3.6", usedefault=True, mandatory=False, argstr="--py %s", desc="which python version to use. default=python3.6") - seg_only = traits.Bool(False, usedefault=False, mandatory=False, argstr="--seg_only", desc="only run FastSurferCNN (generate segmentation, do not run the surface pipeline)") - surf_only = traits.Bool(False, usedefault=False, mandatory=False, argstr="--surf_only", desc="only run the surface pipeline recon_surf. The segmentation created by FastSurferCNN must already exist in this case.") + sd = Directory( + exists=True, + argstr="--sd %s", + mandatory=True, + desc="Subjects directory" + ) + sid = traits.String( + exists=True, + argstr="--sid %s", + mandatory=True, + desc="Subject ID" + ) + t1 = File( + exists=True, + mandatory=True, + argstr="--t1 %s", + desc="T1 full head input (not bias corrected, global path)" + ) + fs_license = File( + exists=True, + mandatory=True, + argstr="--fs_license %s", + desc="Path to FreeSurfer license key file." + ) + seg = File( + exists=True, + mandatory=False, + argstr="--seg %s", + desc="Global path with filename of segmentation" + ) + weights_sag = File( + exists=True, + mandatory=False, + default="../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", + usedefault=False, argstr="--weights_sag %s", + desc="Pretrained weights of sagittal network") + weights_ax = File( + exists=True, + mandatory=False, + default="../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", + usedefault=False, + argstr="--weights_ax %s", + desc="Pretrained weights of axial network", + ) + weights_cor = File( + exists=True, + mandatory=False, + default="../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", + usedefault=False, + argstr="--weights_cor %s", + desc="Pretrained weights of coronal network" + ) + seg_log = File( + exists=True, + mandatory=False, + argstr="--seg_log %s", + desc="Name and location for the log-file for the segmentation (FastSurferCNN)." + ) + clean_seg = traits.Bool( + False, + mandatory=False, + usedefault=False, + argstr="--clean_seg", + desc="Flag to clean up FastSurferCNN segmentation" + ) + run_viewagg_on = File( + exists=True, + mandatory=False, + argstr="--run_viewagg_on %s", + desc="Define where the view aggregation should be run on. " + ) + no_cuda = traits.Bool( + False, + mandatory=False, + usedefault=False, + argstr="--no_cuda", + desc="Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU)" + ) + batch = traits.Int( + 16, + usedefault=True, + mandatory=False, + argstr="--batch %d", + desc="Batch size for inference. default=16. Lower this to reduce memory requirement" + ) + order = traits.Int( + 1, + mandatory=False, + argstr = "--order %d", + usedefault=True, + desc="""Order of interpolation for mri_convert T1 before segmentation + (0=nearest, 1=linear(default), 2=quadratic, 3=cubic)""" + ) + fstess = traits.Bool( + False, + usedefault=False, + mandatory=False, + argstr="--fstess", + desc="Use mri_tesselate instead of marching cube (default) for surface creation" + ) + fsqsphere = traits.Bool( + False, + usedefault=False, + mandatory=False, + argstr="--fsqsphere", + desc="Use FreeSurfer default instead of novel spectral spherical projection for qsphere" + ) + fsaparc = traits.Bool( + False, + usedefault=False, + mandatory=False, + argstr="--fsaparc", + desc="Use FS aparc segmentations in addition to DL prediction" + ) + surfreg = traits.Bool( + True, + usedefault=True, + mandatory=False, + argstr="--surfreg", + desc="""Create Surface-Atlas (sphere.reg) registration with FreeSurfer + (for cross-subject correspondence or other mappings)""" + ) + parallel = traits.Bool( + True, + usedefault=True, + mandatory=False, + argstr="--parallel", + desc="Run both hemispheres in parallel" + ) + threads = traits.Int( + 4, + usedefault=True, + mandatory=False, + argstr="--threads %d", + desc="Set openMP and ITK threads to" + ) + py = traits.String( + "python3.6", + usedefault=True, + mandatory=False, + argstr="--py %s", + desc="which python version to use. default=python3.6" + ) + seg_only = traits.Bool( + False, + usedefault=False, + mandatory=False, + argstr="--seg_only", + desc="only run FastSurferCNN (generate segmentation, do not surface)" + ) + surf_only = traits.Bool( + False, + usedefault=False, + mandatory=False, + argstr="--surf_only", + desc="only run the surface pipeline recon_surf." + ) + class FastSTraitedOutputSpec(TraitedSpec): """ - Outputs directory within the FastSurfer subjects_dir/subject_id/ with structure equivalent to Freesurfer + Outputs directory within the FastSurfer subjects_dir/subject_id/ \ + with structure equivalent to Freesurfer """ - outputs = Directory(exists=True, desc="FastSurfer CNN (or VINN when added) + Surface Pipeline equivalent of the Freesurfer recon-all outputs") + outputs = Directory( + exists=True, + desc="FastSurfer CNN + Surface Pipeline equivalent of recon-all outputs") + class FastSCommand(CommandLine): input_spec = FastSInputSpec @@ -100,4 +267,3 @@ class FastSCommand(CommandLine): def _list_outputs(self): outputs = self.output_spec().get() return outputs - From a7c4aa6aa1a5acee9461b40adc0c4ee06cb49968 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:46:01 -0500 Subject: [PATCH 021/220] Fix formatting for PEP8 compliance --- smriprep/interfaces/fastsurfer.py | 250 +++++++++++++++--------------- 1 file changed, 126 insertions(+), 124 deletions(-) diff --git a/smriprep/interfaces/fastsurfer.py b/smriprep/interfaces/fastsurfer.py index 2c9267c937..9bfb3c585e 100644 --- a/smriprep/interfaces/fastsurfer.py +++ b/smriprep/interfaces/fastsurfer.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: -"""The FastSufer module provides basic functions +"""The FastSufer module provides basic functions for running FastSurfer CNN and surface processing. Examples @@ -32,231 +32,233 @@ class FastSInputSpec(CommandLineInputSpec): Required arguments ------------------ --sd: Output directory $SUBJECTS_DIR - --sid: Subject ID for directory inside $SUBJECTS_DIR to be created - --t1: T1 full head input (not bias corrected, global path). \ - The network was trained with conformed images (UCHAR, 256x256x256, \ - 1 mm voxels and standard slice orientation). \ - These specifications are checked in the eval.py script and the image \ + --sid: Subject ID for directory inside $SUBJECTS_DIR to be created + --t1: T1 full head input (not bias corrected, global path). + The network was trained with conformed images (UCHAR, 256x256x256, + 1 mm voxels and standard slice orientation). + These specifications are checked in the eval.py script and the image is automatically conformed if it does not comply. - --fs_license: Path to FreeSurfer license key file. \ - Register at https://surfer.nmr.mgh.harvard.edu/registration.html \ + --fs_license: Path to FreeSurfer license key file. + Register at https://surfer.nmr.mgh.harvard.edu/registration.html to obtain it if you do not have FreeSurfer installed so far. - + Optional arguments ------------------ Network specific arguments: - --seg: Global path with filename of segmentation \ - (where and under which name to store it). \ - Default location: \ + --seg: Global path with filename of segmentation + (where and under which name to store it). + Default location: $SUBJECTS_DIR/$sid/mri/aparc.DKTatlas+aseg.deep.mgz - --weights_sag: Pretrained weights of sagittal network. \ - Default: \ + --weights_sag: Pretrained weights of sagittal network. + Default: ../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl - --weights_ax: Pretrained weights of axial network. \ - Default: \ + --weights_ax: Pretrained weights of axial network. + Default: ../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl - --weights_cor: Pretrained weights of coronal network. \ + --weights_cor: Pretrained weights of coronal network. Default: ../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl - --seg_log: Name and location for the log-file for the segmentation (FastSurferCNN). \ + --seg_log: Name and location for the log-file for the segmentation (FastSurferCNN). Default: $SUBJECTS_DIR/$sid/scripts/deep-seg.log --clean_seg: Flag to clean up FastSurferCNN segmentation - --run_viewagg_on: Define where the view aggregation should be run on. \ - By default, the program checks if you have enough memory to run \ - the view aggregation on the gpu. The total memory is considered for this decision. \ - If this fails, or you actively overwrote the check with setting \ - "--run_viewagg_on cpu", view agg is run on the cpu. \ - Equivalently, if you define "--run_viewagg_on gpu", view agg will be run on the gpu \ + --run_viewagg_on: Define where the view aggregation should be run on. + By default, the program checks if you have enough memory to run + the view aggregation on the gpu. The total memory is considered for this decision. + If this fails, or you actively overwrote the check with setting + "--run_viewagg_on cpu", view agg is run on the cpu. + Equivalently, if you define "--run_viewagg_on gpu", view agg will be run on the gpu (no memory check will be done). --no_cuda: Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU) --batch: Batch size for inference. Default: 16. Lower this to reduce memory requirement - --order: Order of interpolation for mri_convert T1 before segmentation \ + --order: Order of interpolation for mri_convert T1 before segmentation (0=nearest, 1=linear(default), 2=quadratic, 3=cubic) Surface pipeline arguments: --fstess: Use mri_tesselate instead of marching cube (default) for surface creation - --fsqsphere: Use FreeSurfer default instead of \ + --fsqsphere: Use FreeSurfer default instead of novel spectral spherical projection for qsphere - --fsaparc: Use FS aparc segmentations in addition to DL prediction \ + --fsaparc: Use FS aparc segmentations in addition to DL prediction (slower in this case and usually the mapped ones from the DL prediction are fine) - --surfreg: Create Surface-Atlas (sphere.reg) registration with FreeSurfer \ + --surfreg: Create Surface-Atlas (sphere.reg) registration with FreeSurfer (for cross-subject correspondence or other mappings) --parallel: Run both hemispheres in parallel - --threads: Set openMP and ITK threads to + --threads: Set openMP and ITK threads Other: --py: which python version to use. Default: python3.6 --seg_only: only run FastSurferCNN (generate segmentation, do not run the surface pipeline) - --surf_only: only run the surface pipeline recon_surf. \ + --surf_only: only run the surface pipeline recon_surf. The segmentation created by FastSurferCNN must already exist in this case. """ sd = Directory( - exists=True, - argstr="--sd %s", - mandatory=True, + exists=True, + argstr="--sd %s", + mandatory=True, desc="Subjects directory" ) sid = traits.String( - exists=True, - argstr="--sid %s", - mandatory=True, + exists=True, + argstr="--sid %s", + mandatory=True, desc="Subject ID" ) t1 = File( - exists=True, - mandatory=True, - argstr="--t1 %s", + exists=True, + mandatory=True, + argstr="--t1 %s", desc="T1 full head input (not bias corrected, global path)" ) fs_license = File( - exists=True, - mandatory=True, - argstr="--fs_license %s", + exists=True, + mandatory=True, + argstr="--fs_license %s", desc="Path to FreeSurfer license key file." ) seg = File( - exists=True, - mandatory=False, - argstr="--seg %s", + exists=True, + mandatory=False, + argstr="--seg %s", desc="Global path with filename of segmentation" ) - weights_sag = File( - exists=True, - mandatory=False, - default="../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", - usedefault=False, argstr="--weights_sag %s", - desc="Pretrained weights of sagittal network") + weights_sag = File( + exists=True, + mandatory=False, + default="../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", + usedefault=False, argstr="--weights_sag %s", + desc="Pretrained weights of sagittal network" + ) weights_ax = File( - exists=True, - mandatory=False, - default="../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", - usedefault=False, - argstr="--weights_ax %s", - desc="Pretrained weights of axial network", + exists=True, + mandatory=False, + default="../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", + usedefault=False, + argstr="--weights_ax %s", + desc="Pretrained weights of axial network" ) weights_cor = File( - exists=True, - mandatory=False, - default="../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", - usedefault=False, - argstr="--weights_cor %s", + exists=True, + mandatory=False, + default="../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl", + usedefault=False, + argstr="--weights_cor %s", desc="Pretrained weights of coronal network" ) seg_log = File( - exists=True, - mandatory=False, - argstr="--seg_log %s", + exists=True, + mandatory=False, + argstr="--seg_log %s", desc="Name and location for the log-file for the segmentation (FastSurferCNN)." ) clean_seg = traits.Bool( False, - mandatory=False, - usedefault=False, - argstr="--clean_seg", + mandatory=False, + usedefault=False, + argstr="--clean_seg", desc="Flag to clean up FastSurferCNN segmentation" ) run_viewagg_on = File( - exists=True, + exists=True, mandatory=False, - argstr="--run_viewagg_on %s", - desc="Define where the view aggregation should be run on. " + argstr="--run_viewagg_on %s", + desc="Define where the view aggregation should be run on." ) - no_cuda = traits.Bool( - False, - mandatory=False, - usedefault=False, - argstr="--no_cuda", + no_cuda = traits.Bool( + False, + mandatory=False, + usedefault=False, + argstr="--no_cuda", desc="Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU)" ) - batch = traits.Int( - 16, - usedefault=True, - mandatory=False, - argstr="--batch %d", + batch = traits.Int( + 16, + usedefault=True, + mandatory=False, + argstr="--batch %d", desc="Batch size for inference. default=16. Lower this to reduce memory requirement" ) order = traits.Int( - 1, - mandatory=False, - argstr = "--order %d", - usedefault=True, - desc="""Order of interpolation for mri_convert T1 before segmentation + 1, + mandatory=False, + argstr="--order %d", + usedefault=True, + desc="""Order of interpolation for mri_convert T1 before segmentation (0=nearest, 1=linear(default), 2=quadratic, 3=cubic)""" ) fstess = traits.Bool( - False, - usedefault=False, - mandatory=False, - argstr="--fstess", + False, + usedefault=False, + mandatory=False, + argstr="--fstess", desc="Use mri_tesselate instead of marching cube (default) for surface creation" ) fsqsphere = traits.Bool( - False, - usedefault=False, - mandatory=False, - argstr="--fsqsphere", + False, + usedefault=False, + mandatory=False, + argstr="--fsqsphere", desc="Use FreeSurfer default instead of novel spectral spherical projection for qsphere" ) fsaparc = traits.Bool( - False, - usedefault=False, + False, + usedefault=False, mandatory=False, - argstr="--fsaparc", + argstr="--fsaparc", desc="Use FS aparc segmentations in addition to DL prediction" ) surfreg = traits.Bool( - True, - usedefault=True, - mandatory=False, - argstr="--surfreg", + True, + usedefault=True, + mandatory=False, + argstr="--surfreg", desc="""Create Surface-Atlas (sphere.reg) registration with FreeSurfer (for cross-subject correspondence or other mappings)""" ) parallel = traits.Bool( - True, - usedefault=True, - mandatory=False, - argstr="--parallel", + True, + usedefault=True, + mandatory=False, + argstr="--parallel", desc="Run both hemispheres in parallel" ) threads = traits.Int( - 4, - usedefault=True, - mandatory=False, - argstr="--threads %d", + 4, + usedefault=True, + mandatory=False, + argstr="--threads %d", desc="Set openMP and ITK threads to" ) py = traits.String( - "python3.6", - usedefault=True, - mandatory=False, - argstr="--py %s", + "python3.6", + usedefault=True, + mandatory=False, + argstr="--py %s", desc="which python version to use. default=python3.6" ) seg_only = traits.Bool( - False, - usedefault=False, - mandatory=False, - argstr="--seg_only", + False, + usedefault=False, + mandatory=False, + argstr="--seg_only", desc="only run FastSurferCNN (generate segmentation, do not surface)" ) surf_only = traits.Bool( - False, - usedefault=False, - mandatory=False, - argstr="--surf_only", + False, + usedefault=False, + mandatory=False, + argstr="--surf_only", desc="only run the surface pipeline recon_surf." ) class FastSTraitedOutputSpec(TraitedSpec): """ - Outputs directory within the FastSurfer subjects_dir/subject_id/ \ + Outputs directory within the FastSurfer subjects_dir/subject_id/ with structure equivalent to Freesurfer """ outputs = Directory( - exists=True, - desc="FastSurfer CNN + Surface Pipeline equivalent of recon-all outputs") + exists=True, + desc="FastSurfer CNN + Surface Pipeline equivalent of recon-all outputs" + ) class FastSCommand(CommandLine): From b5cf10a983c408e1f5f27f033dd4ed5946a98b85 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:46:40 -0500 Subject: [PATCH 022/220] Fix formatting for PEP8 compliance --- smriprep/workflows/anatomical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py index b7b35ac3d9..715362893a 100644 --- a/smriprep/workflows/anatomical.py +++ b/smriprep/workflows/anatomical.py @@ -48,8 +48,8 @@ from niworkflows.anat.ants import init_brain_extraction_wf, init_n4_only_wf from ..utils.bids import get_outputnode_spec from ..utils.misc import ( - apply_lut as _apply_bids_lut, - fs_isRunning as _fs_isRunning, + apply_lut as _apply_bids_lut, + fs_isRunning as _fs_isRunning, check_fastsurfer as _check_fastsurfer ) from .norm import init_anat_norm_wf From a93fd77a5198e85602a0ae3d3b262221ce0d5c7a Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:47:56 -0500 Subject: [PATCH 023/220] Fix formatting for PEP8 compliance --- smriprep/interfaces/fastsurfer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/smriprep/interfaces/fastsurfer.py b/smriprep/interfaces/fastsurfer.py index 9bfb3c585e..fb2d9b41d2 100644 --- a/smriprep/interfaces/fastsurfer.py +++ b/smriprep/interfaces/fastsurfer.py @@ -34,7 +34,7 @@ class FastSInputSpec(CommandLineInputSpec): --sd: Output directory $SUBJECTS_DIR --sid: Subject ID for directory inside $SUBJECTS_DIR to be created --t1: T1 full head input (not bias corrected, global path). - The network was trained with conformed images (UCHAR, 256x256x256, + The network was trained with conformed images (UCHAR, 256x256x256, 1 mm voxels and standard slice orientation). These specifications are checked in the eval.py script and the image is automatically conformed if it does not comply. @@ -119,7 +119,7 @@ class FastSInputSpec(CommandLineInputSpec): mandatory=False, argstr="--seg %s", desc="Global path with filename of segmentation" - ) + ) weights_sag = File( exists=True, mandatory=False, @@ -210,7 +210,7 @@ class FastSInputSpec(CommandLineInputSpec): usedefault=True, mandatory=False, argstr="--surfreg", - desc="""Create Surface-Atlas (sphere.reg) registration with FreeSurfer + desc="""Create Surface-Atlas (sphere.reg) registration with FreeSurfer (for cross-subject correspondence or other mappings)""" ) parallel = traits.Bool( From 41cfba63ff88e1b1d2a8b23ec5eef11b346e0b1e Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 18:37:50 -0500 Subject: [PATCH 024/220] Fix typo --- smriprep/cli/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smriprep/cli/run.py b/smriprep/cli/run.py index 223fdc071d..24cbab2e27 100644 --- a/smriprep/cli/run.py +++ b/smriprep/cli/run.py @@ -222,7 +222,7 @@ def get_parser(): dest="hires", help="disable sub-millimeter (hires) reconstruction", ) - g_surfs_xor.add_argument( + g_surfs.add_argument( "--fastsurfer-recon", action="store_true", dest="run_fastsurfer", From 8f821d2152e5c7537ea2f9eb881e8425700924e8 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 18:39:54 -0500 Subject: [PATCH 025/220] Revert Convert3D install method from development use case --- Dockerfile | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3f889f0c56..f73bafd2c4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -105,7 +105,7 @@ ENV PATH /opt/conda/bin:$PATH RUN python3.6 -m pip install -U git+https://github.com/Deep-MI/LaPy.git#egg=lapy # Add FastSurfer (copy application code) to docker image -RUN cd /opt && git clone https://github.com/Deep-MI/FastSurfer.git +RUN cd /opt && git clone https://github.com/Deep-MI/FastSurfer.git ENV FASTSURFER_HOME=/opt/FastSurfer @@ -216,12 +216,10 @@ ENV LANG="en_US.UTF-8" \ LANGUAGE="en_US:en" RUN ls /etc/ssl/certs/ - -# Convert3D (neurodocker build) - adapted to allow for local copy of Convert3D -# my build machine was giving ca-certificates errors for fetching C3D from sourceforge in build -#RUN echo "Downloading Convert3D ..." \ -# && mkdir -p /opt/convert3d-1.0.0 \ -# && cd /opt/ && curl -fsSL https://sourceforge.net/projects/c3d/files/c3d/1.0.0/c3d-1.0.0-Linux-x86_64.tar.gz \ +# Convert3D (neurodocker build) +RUN echo "Downloading Convert3D ..." \ + && mkdir -p /opt/convert3d-1.0.0 \ + && cd /opt/ && curl -fsSL https://sourceforge.net/projects/c3d/files/c3d/1.0.0/c3d-1.0.0-Linux-x86_64.tar.gz \ COPY c3d-1.0.0-Linux-x86_64.tar.gz /opt/c3d-1.0.0-Linux-x86_64.tar.gz RUN cd /opt/ && mkdir -p /opt/convert3d-1.0.0 && tar -xzf c3d-1.0.0-Linux-x86_64.tar.gz -C /opt/convert3d-1.0.0 --strip-components 1 \ --exclude "c3d-1.0.0-Linux-x86_64/lib" \ From e3380019f2776f175767ebde7283b202363c956e Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 20:01:27 -0500 Subject: [PATCH 026/220] Fix Convert3D --- Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index f73bafd2c4..821b6727b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -219,9 +219,8 @@ RUN ls /etc/ssl/certs/ # Convert3D (neurodocker build) RUN echo "Downloading Convert3D ..." \ && mkdir -p /opt/convert3d-1.0.0 \ - && cd /opt/ && curl -fsSL https://sourceforge.net/projects/c3d/files/c3d/1.0.0/c3d-1.0.0-Linux-x86_64.tar.gz \ -COPY c3d-1.0.0-Linux-x86_64.tar.gz /opt/c3d-1.0.0-Linux-x86_64.tar.gz -RUN cd /opt/ && mkdir -p /opt/convert3d-1.0.0 && tar -xzf c3d-1.0.0-Linux-x86_64.tar.gz -C /opt/convert3d-1.0.0 --strip-components 1 \ + && curl -fsSL --retry 5 https://sourceforge.net/projects/c3d/files/c3d/1.0.0/c3d-1.0.0-Linux-x86_64.tar.gz/download \ + | tar -xz -C /opt/convert3d-1.0.0 --strip-components 1 \ --exclude "c3d-1.0.0-Linux-x86_64/lib" \ --exclude "c3d-1.0.0-Linux-x86_64/share" \ --exclude "c3d-1.0.0-Linux-x86_64/bin/c3d_gui" From 16d5f0937c0e4ce6f95f744c54e075a4a7fb3942 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 21:05:42 -0500 Subject: [PATCH 027/220] Pin Jinja2 version to prevent CircleCI build_docs failure Fix fail to build docs in CircleCI observed in testing draft changes made to PR #280. ``` #!/bin/bash -eo pipefail make -C docs SPHINXOPTS="-W" BUILDDIR="_build/no_version_html" html make: Entering directory '/tmp/gh-pages/docs' PYTHONPATH=/tmp/gh-pages sphinx-build -b html -d _build/no_version_html/doctrees -W . _build/no_version_html/html Traceback (most recent call last): File "/home/circleci/.local/bin/sphinx-build", line 5, in from sphinx.cmd.build import main File "/home/circleci/.local/lib/python3.8/site-packages/sphinx/cmd/build.py", line 23, in from sphinx.application import Sphinx File "/home/circleci/.local/lib/python3.8/site-packages/sphinx/application.py", line 42, in from sphinx.highlighting import lexer_classes, lexers File "/home/circleci/.local/lib/python3.8/site-packages/sphinx/highlighting.py", line 30, in from sphinx.ext import doctest File "/home/circleci/.local/lib/python3.8/site-packages/sphinx/ext/doctest.py", line 28, in from sphinx.builders import Builder File "/home/circleci/.local/lib/python3.8/site-packages/sphinx/builders/__init__.py", line 24, in from sphinx.io import read_doc File "/home/circleci/.local/lib/python3.8/site-packages/sphinx/io.py", line 42, in from sphinx.util.rst import append_epilog, docinfo_re, prepend_prolog File "/home/circleci/.local/lib/python3.8/site-packages/sphinx/util/rst.py", line 22, in from jinja2 import environmentfilter ImportError: cannot import name 'environmentfilter' from 'jinja2' (/home/circleci/.local/lib/python3.8/site-packages/jinja2/__init__.py) make: *** [Makefile:61: html] Error 1 make: Leaving directory '/tmp/gh-pages/docs' Exited with code exit status 2 CircleCI received exit code 2 ``` From suggested fix for similar behavior: https://github.com/mkdocs/mkdocs/issues/2799 --- docs/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index 3d2ac3a34a..aa1c4a6999 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -11,3 +11,4 @@ sphinx >= 2.1.2, < 3 sphinx_rtd_theme sphinxcontrib-apidoc ~= 0.3.0 templateflow +jinja2==3.0.3 From 6973ddd34a34532747e67a6ae40a86e660cf7871 Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 21:14:22 -0500 Subject: [PATCH 028/220] Fix docstring Fix docstring list end without a blank line error --- smriprep/interfaces/fastsurfer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/smriprep/interfaces/fastsurfer.py b/smriprep/interfaces/fastsurfer.py index fb2d9b41d2..3fa54d3bd8 100644 --- a/smriprep/interfaces/fastsurfer.py +++ b/smriprep/interfaces/fastsurfer.py @@ -28,9 +28,10 @@ class FastSInputSpec(CommandLineInputSpec): - """ + r""" Required arguments ------------------ + --sd: Output directory $SUBJECTS_DIR --sid: Subject ID for directory inside $SUBJECTS_DIR to be created --t1: T1 full head input (not bias corrected, global path). @@ -44,6 +45,7 @@ class FastSInputSpec(CommandLineInputSpec): Optional arguments ------------------ + Network specific arguments: --seg: Global path with filename of segmentation (where and under which name to store it). @@ -89,6 +91,7 @@ class FastSInputSpec(CommandLineInputSpec): (generate segmentation, do not run the surface pipeline) --surf_only: only run the surface pipeline recon_surf. The segmentation created by FastSurferCNN must already exist in this case. + """ sd = Directory( exists=True, From d35eb072613715965b1b855142075397786801af Mon Sep 17 00:00:00 2001 From: pcamach2 <49655443+pcamach2@users.noreply.github.com> Date: Tue, 26 Apr 2022 21:34:10 -0500 Subject: [PATCH 029/220] Update example of commandline FastSurfer usage to docstring Update example of commandline FastSurfer usage to docstring, formatting of input spec docstring --- smriprep/interfaces/fastsurfer.py | 154 +++++++++++++++++++----------- 1 file changed, 97 insertions(+), 57 deletions(-) diff --git a/smriprep/interfaces/fastsurfer.py b/smriprep/interfaces/fastsurfer.py index 3fa54d3bd8..f6a04f555d 100644 --- a/smriprep/interfaces/fastsurfer.py +++ b/smriprep/interfaces/fastsurfer.py @@ -28,69 +28,98 @@ class FastSInputSpec(CommandLineInputSpec): - r""" + """ Required arguments - ------------------ + ================== - --sd: Output directory $SUBJECTS_DIR - --sid: Subject ID for directory inside $SUBJECTS_DIR to be created - --t1: T1 full head input (not bias corrected, global path). - The network was trained with conformed images (UCHAR, 256x256x256, - 1 mm voxels and standard slice orientation). - These specifications are checked in the eval.py script and the image - is automatically conformed if it does not comply. - --fs_license: Path to FreeSurfer license key file. - Register at https://surfer.nmr.mgh.harvard.edu/registration.html - to obtain it if you do not have FreeSurfer installed so far. + sd + Output directory $SUBJECTS_DIR + sid + Subject ID for directory inside $SUBJECTS_DIR to be created + t1 + T1 full head input (not bias corrected, global path). + The network was trained with conformed images (UCHAR, 256x256x256, + 1 mm voxels and standard slice orientation). + These specifications are checked in the eval.py script and the image + is automatically conformed if it does not comply. + fs_license + Path to FreeSurfer license key file. + Register at https://surfer.nmr.mgh.harvard.edu/registration.html + to obtain it if you do not have FreeSurfer installed so far. Optional arguments - ------------------ + ================== + + Network specific arguments + -------------------------- + + seg + Global path with filename of segmentation + (where and under which name to store it). + Default location + $SUBJECTS_DIR/$sid/mri/aparc.DKTatlas+aseg.deep.mgz + weights_sag + Pretrained weights of sagittal network. + Default + ../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl + weights_ax + Pretrained weights of axial network. + Default + ../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl + weights_cor + Pretrained weights of coronal network. + Default ../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl + seg_log + Name and location for the log-file for the segmentation (FastSurferCNN). + Default $SUBJECTS_DIR/$sid/scripts/deep-seg.log + clean_seg + Flag to clean up FastSurferCNN segmentation + run_viewagg_on + Define where the view aggregation should be run on. + By default, the program checks if you have enough memory to run + the view aggregation on the gpu. The total memory is considered for this decision. + If this fails, or you actively overwrote the check with setting + "run_viewagg_on cpu", view agg is run on the cpu. + Equivalently, if you define "--run_viewagg_on gpu", view agg will be run on the gpu + (no memory check will be done). + no_cuda + Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU) + batch + Batch size for inference. Default: 16. Lower this to reduce memory requirement + order + Order of interpolation for mri_convert T1 before segmentation + (0=nearest, 1=linear(default), 2=quadratic, 3=cubic) - Network specific arguments: - --seg: Global path with filename of segmentation - (where and under which name to store it). - Default location: - $SUBJECTS_DIR/$sid/mri/aparc.DKTatlas+aseg.deep.mgz - --weights_sag: Pretrained weights of sagittal network. - Default: - ../checkpoints/Sagittal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl - --weights_ax: Pretrained weights of axial network. - Default: - ../checkpoints/Axial_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl - --weights_cor: Pretrained weights of coronal network. - Default: ../checkpoints/Coronal_Weights_FastSurferCNN/ckpts/Epoch_30_training_state.pkl - --seg_log: Name and location for the log-file for the segmentation (FastSurferCNN). - Default: $SUBJECTS_DIR/$sid/scripts/deep-seg.log - --clean_seg: Flag to clean up FastSurferCNN segmentation - --run_viewagg_on: Define where the view aggregation should be run on. - By default, the program checks if you have enough memory to run - the view aggregation on the gpu. The total memory is considered for this decision. - If this fails, or you actively overwrote the check with setting - "--run_viewagg_on cpu", view agg is run on the cpu. - Equivalently, if you define "--run_viewagg_on gpu", view agg will be run on the gpu - (no memory check will be done). - --no_cuda: Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU) - --batch: Batch size for inference. Default: 16. Lower this to reduce memory requirement - --order: Order of interpolation for mri_convert T1 before segmentation - (0=nearest, 1=linear(default), 2=quadratic, 3=cubic) + Surface pipeline arguments + -------------------------- - Surface pipeline arguments: - --fstess: Use mri_tesselate instead of marching cube (default) for surface creation - --fsqsphere: Use FreeSurfer default instead of - novel spectral spherical projection for qsphere - --fsaparc: Use FS aparc segmentations in addition to DL prediction - (slower in this case and usually the mapped ones from the DL prediction are fine) - --surfreg: Create Surface-Atlas (sphere.reg) registration with FreeSurfer - (for cross-subject correspondence or other mappings) - --parallel: Run both hemispheres in parallel - --threads: Set openMP and ITK threads + fstess + Use mri_tesselate instead of marching cube (default) for surface creation + fsqsphere + Use FreeSurfer default instead of + novel spectral spherical projection for qsphere + fsaparc + Use FS aparc segmentations in addition to DL prediction + (slower in this case and usually the mapped ones from the DL prediction are fine) + surfreg + Create Surface-Atlas (sphere.reg) registration with FreeSurfer + (for cross-subject correspondence or other mappings) + parallel + Run both hemispheres in parallel + threads + Set openMP and ITK threads - Other: - --py: which python version to use. Default: python3.6 - --seg_only: only run FastSurferCNN - (generate segmentation, do not run the surface pipeline) - --surf_only: only run the surface pipeline recon_surf. - The segmentation created by FastSurferCNN must already exist in this case. + Other + ---- + + py + which python version to use. Default: python3.6 + seg_only + only run FastSurferCNN + (generate segmentation, do not run the surface pipeline) + surf_only + only run the surface pipeline recon_surf. + The segmentation created by FastSurferCNN must already exist in this case. """ sd = Directory( @@ -256,7 +285,8 @@ class FastSInputSpec(CommandLineInputSpec): class FastSTraitedOutputSpec(TraitedSpec): """ Outputs directory within the FastSurfer subjects_dir/subject_id/ - with structure equivalent to Freesurfer + with structure equivalent to Freesurfer + """ outputs = Directory( exists=True, @@ -265,6 +295,16 @@ class FastSTraitedOutputSpec(TraitedSpec): class FastSCommand(CommandLine): + """ + Wraps FastSurfer command for segmentation and surface processing. + + Example basic commandline usage:: + + $ /opt/FastSurfer/run_fastsurfer.sh --fs_license /fs60/license \ + --t1 /data/sub-