├── .circleci └── config.yml ├── .codecov.yml ├── .coveragerc ├── .docker ├── fsl-6.0 │ ├── bin │ │ ├── eddy_openmp │ │ ├── imglob │ │ └── topup │ └── lib │ │ ├── libgfortran.so.3 │ │ └── libopenblas.so.0 └── neurodebian.gpg ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE.md ├── config.yml └── workflows │ └── pythonpackage.yml ├── .gitignore ├── .mailmap ├── .maint ├── contributors.json ├── developers.json ├── former.json ├── paper_author_list.py ├── update_changes.sh └── update_zenodo.py ├── .pep8speaks.yml ├── .zenodo.json ├── CHANGES.rst ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── Makefile ├── NOTICE ├── README.rst ├── dmriprep ├── __about__.py ├── __init__.py ├── _version.py ├── cli │ ├── __init__.py │ ├── parser.py │ ├── run.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_parser.py │ │ └── test_version.py │ ├── version.py │ └── workflow.py ├── config │ ├── __init__.py │ ├── reports-spec.yml │ └── testing.py ├── conftest.py ├── data │ ├── __init__.py │ ├── boilerplate.bib │ ├── flirtsch │ │ ├── b02b0.cnf │ │ ├── b02b0_1.cnf │ │ ├── b02b0_2.cnf │ │ ├── b02b0_4.cnf │ │ └── b02b0_quick.cnf │ └── tests │ │ ├── THP │ │ ├── CHANGES │ │ ├── README │ │ ├── dataset_description.json │ │ └── sub-THP0005 │ │ │ ├── anat │ │ │ ├── sub-THP0005_T1w.json │ │ │ └── sub-THP0005_T1w.nii.gz │ │ │ └── dwi │ │ │ ├── sub-THP0005_dwi.bval │ │ │ ├── sub-THP0005_dwi.bvec │ │ │ ├── sub-THP0005_dwi.json │ │ │ └── sub-THP0005_dwi.nii.gz │ │ ├── bval │ │ ├── bvec │ │ ├── config.toml │ │ ├── dwi.nii.gz │ │ ├── dwi.tsv │ │ └── dwi_mask.nii.gz ├── interfaces │ ├── __init__.py │ ├── images.py │ ├── reports.py │ └── vectors.py ├── utils │ ├── __init__.py │ ├── bids.py │ ├── images.py │ ├── misc.py │ ├── tests │ │ ├── __init__.py │ │ └── test_vectors.py │ └── vectors.py └── workflows │ ├── __init__.py │ ├── base.py │ └── dwi │ ├── __init__.py │ ├── base.py │ ├── eddy.py │ └── outputs.py ├── docs ├── Makefile ├── _static │ ├── RTD-advanced-conf.png │ └── git.png ├── api.rst ├── changes.rst ├── conf.py ├── index.rst ├── installation.rst ├── links.rst ├── reference │ └── .gitignore ├── requirements.txt ├── resources │ ├── figure1.png │ └── figure1.svg ├── roadmap.rst ├── sphinxext │ ├── docscrape.py │ ├── docscrape_sphinx.py │ ├── github.py │ ├── github_link.py │ ├── math_dollar.py │ └── numpydoc.py ├── tools │ ├── LICENSE.txt │ ├── apigen.py │ └── buildmodref.py └── usage.rst ├── get_version.py ├── pyproject.toml ├── setup.cfg ├── setup.py └── versioneer.py /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | range: "0...100" 3 | ignore: 4 | - "**/data" 5 | - "**/tests" 6 | - "setup.py" -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | omit = 4 | */tests/* 5 | */_afq/* 6 | dmriprep/_version.py 7 | dmriprep/conftest.py 8 | 9 | [report] 10 | # Regexes for lines to exclude from consideration 11 | exclude_lines = 12 | raise NotImplementedError 13 | warnings\.warn -------------------------------------------------------------------------------- /.docker/fsl-6.0/bin/eddy_openmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/.docker/fsl-6.0/bin/eddy_openmp -------------------------------------------------------------------------------- /.docker/fsl-6.0/bin/imglob: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # imglob - expand list of image filenames 3 | # Stephen Smith, Mark Jenkinson and Matthew Webster FMRIB Image Analysis Group 4 | # Copyright (C) 2009 University of Oxford 5 | # Part of FSL - FMRIB's Software Library 6 | # http://www.fmrib.ox.ac.uk/fsl 7 | # fsl@fmrib.ox.ac.uk 8 | # 9 | # Developed at FMRIB (Oxford Centre for Functional Magnetic Resonance 10 | # Imaging of the Brain), Department of Clinical Neurology, Oxford 11 | # University, Oxford, UK 12 | # 13 | # 14 | # LICENCE 15 | # 16 | # FMRIB Software Library, Release 5.0 (c) 2012, The University of 17 | # Oxford (the "Software") 18 | # 19 | # The Software remains the property of the University of Oxford ("the 20 | # University"). 21 | # 22 | # The Software is distributed "AS IS" under this Licence solely for 23 | # non-commercial use in the hope that it will be useful, but in order 24 | # that the University as a charitable foundation protects its assets for 25 | # the benefit of its educational and research purposes, the University 26 | # makes clear that no condition is made or to be implied, nor is any 27 | # warranty given or to be implied, as to the accuracy of the Software, 28 | # or that it will be suitable for any particular purpose or for use 29 | # under any specific conditions. Furthermore, the University disclaims 30 | # all responsibility for the use which is made of the Software. It 31 | # further disclaims any liability for the outcomes arising from using 32 | # the Software. 33 | # 34 | # The Licensee agrees to indemnify the University and hold the 35 | # University harmless from and against any and all claims, damages and 36 | # liabilities asserted by third parties (including claims for 37 | # negligence) which arise directly or indirectly from the use of the 38 | # Software or the sale of any products based on the Software. 39 | # 40 | # No part of the Software may be reproduced, modified, transmitted or 41 | # transferred in any form or by any means, electronic or mechanical, 42 | # without the express permission of the University. The permission of 43 | # the University is not required if the said reproduction, modification, 44 | # transmission or transference is done without financial return, the 45 | # conditions of this Licence are imposed upon the receiver of the 46 | # product, and all original and amended source code is included in any 47 | # transmitted product. You may be held legally responsible for any 48 | # copyright infringement that is caused or encouraged by your failure to 49 | # abide by these terms and conditions. 50 | # 51 | # You are not permitted under this Licence to use this Software 52 | # commercially. Use for which any financial return is received shall be 53 | # defined as commercial use, and includes (1) integration of all or part 54 | # of the source code or the Software into a product for sale or license 55 | # by or on behalf of Licensee to third parties or (2) use of the 56 | # Software or any derivative of it for research with the final aim of 57 | # developing software products for sale or license to a third party or 58 | # (3) use of the Software or any derivative of it for research with the 59 | # final aim of developing non-software products for sale or license to a 60 | # third party, or (4) use of the Software to provide any service to an 61 | # external organisation for which payment is received. If you are 62 | # interested in using the Software commercially, please contact Isis 63 | # Innovation Limited ("Isis"), the technology transfer company of the 64 | # University, to negotiate a licence. Contact details are: 65 | # innovation@isis.ox.ac.uk quoting reference DE/9564. 66 | 67 | import sys 68 | import os 69 | import glob 70 | 71 | setAvailable=True 72 | if sys.version_info < (2, 4): 73 | import sets 74 | from sets import Set 75 | setAvailable=False 76 | 77 | def usage(): 78 | print("Usage: $0 [-extension/extensions] ") 79 | print(" -extension for one image with full extension") 80 | print(" -extensions for image list with full extensions") 81 | sys.exit(1) 82 | 83 | def isImage(input,allExtensions): #Returns whether an input filename has an image extension ( and the basename and extension pair ) 84 | for extension in allExtensions: 85 | if input[-len(extension):] == extension: 86 | return True, input[:-len(extension)], extension 87 | return False, input, '' 88 | 89 | def removeImageExtension(input,allExtensions): 90 | return isImage(input,allExtensions)[1] 91 | 92 | if len(sys.argv) <= 1: 93 | usage() 94 | 95 | deleteExtensions=True 96 | primaryExtensions=['.nii.gz', '.nii', '.hdr.gz', '.hdr'] 97 | secondaryExtensions=['.img.gz', '.img'] 98 | allExtensions=primaryExtensions+secondaryExtensions 99 | validExtensions=primaryExtensions 100 | startingArg=1 101 | 102 | if sys.argv[1] == "-extensions": 103 | validExtensions=allExtensions 104 | deleteExtensions=False 105 | startingArg=2 106 | if sys.argv[1] == "-extension": 107 | deleteExtensions=False 108 | startingArg=2 109 | 110 | filelist=[] 111 | for arg in range(startingArg, len(sys.argv)): 112 | # if isImage(sys.argv[arg],allExtensions)[0]: #These enable a "pedantic" style mode currently not used 113 | # filelist.extend(glob.glob(sys.argv[arg])) 114 | # else: 115 | # for currentExtension in validExtensions: 116 | # filelist.extend(glob.glob(sys.argv[arg]+currentExtension)) 117 | for currentExtension in validExtensions: 118 | filelist.extend(glob.glob(removeImageExtension(sys.argv[arg],allExtensions)+currentExtension)) 119 | 120 | if deleteExtensions: 121 | for file in range(0, len(filelist)): 122 | filelist[file]=removeImageExtension(filelist[file],allExtensions) 123 | if setAvailable: 124 | filelist=list(set(filelist)) 125 | else: 126 | filelist=list(Set(filelist)) 127 | filelist.sort() 128 | 129 | for file in range(0, len(filelist)): 130 | print(filelist[file], end=' ') 131 | if file < len(filelist)-1: 132 | print(" ", end=' ') 133 | -------------------------------------------------------------------------------- /.docker/fsl-6.0/bin/topup: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/.docker/fsl-6.0/bin/topup -------------------------------------------------------------------------------- /.docker/fsl-6.0/lib/libgfortran.so.3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/.docker/fsl-6.0/lib/libgfortran.so.3 -------------------------------------------------------------------------------- /.docker/fsl-6.0/lib/libopenblas.so.0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/.docker/fsl-6.0/lib/libopenblas.so.0 -------------------------------------------------------------------------------- /.docker/neurodebian.gpg: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v1 3 | 4 | mQGiBEQ7TOgRBADvaRsIZ3VZ6Qy7PlDpdMm97m0OfvouOj/HhjOM4M3ECbGn4cYh 5 | vN1gK586s3sUsUcNQ8LuWvNsYhxYsVTZymCReJMEDxod0U6/z/oIbpWv5svF3kpl 6 | ogA66Ju/6cZx62RiCSOkskI6A3Waj6xHyEo8AGOPfzbMoOOQ1TS1u9s2FwCgxziL 7 | wADvKYlDZnWM03QtqIJVD8UEAOks9Q2OqFoqKarj6xTRdOYIBVEp2jhozZUZmLmz 8 | pKL9E4NKGfixqxdVimFcRUGM5h7R2w7ORqXjCzpiPmgdv3jJLWDnmHLmMYRYQc8p 9 | 5nqo8mxuO3zJugxBemWoacBDd1MJaH7nK20Hsk9L/jvU/qLxPJotMStTnwO+EpsK 10 | HlihA/9ZpvzR1QWNUd9nSuNR3byJhaXvxqQltsM7tLqAT4qAOJIcMjxr+qESdEbx 11 | NHM5M1Y21ZynrsQw+Fb1WHXNbP79vzOxHoZR0+OXe8uUpkri2d9iOocre3NUdpOO 12 | JHtl6cGGTFILt8tSuOVxMT/+nlo038JQB2jARe4B85O0tkPIPbQybmV1cm8uZGVi 13 | aWFuLm5ldCBhcmNoaXZlIDxtaWNoYWVsLmhhbmtlQGdtYWlsLmNvbT6IRgQQEQgA 14 | BgUCTVHJKwAKCRCNEUVjdcAkyOvzAJ0abJz+f2a6VZG1c9T8NHMTYh1atwCgt0EE 15 | 3ZZd/2in64jSzu0miqhXbOKISgQQEQIACgUCSotRlwMFAXgACgkQ93+NsjFEvg8n 16 | JgCfWcdJbILBtpLZCocvOzlLPqJ0Fn0AoI4EpJRxoUnrtzBGUC1MqecU7WsDiGAE 17 | ExECACAFAkqLUWcCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRCl0y8BJkml 18 | qVklAJ4h2V6MdQkSAThF5c2Gkq6eSoIQYQCeM0DWyB9Bl+tTPSTYXwwZi2uoif20 19 | QmFwc3kuZ3NlLnVuaS1tYWdkZWJ1cmcuZGUgRGViaWFuIEFyY2hpdmUgPG1pY2hh 20 | ZWwuaGFua2VAZ21haWwuY29tPohGBBARAgAGBQJEO03FAAoJEPd/jbIxRL4PU18A 21 | n3tn7i4qdlMi8kHbYWFoabsKc9beAJ9sl/leZNCYNMGhz+u6BQgyeLKw94heBBMR 22 | AgAeBQJEO0zoAhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEKXTLwEmSaWpVdoA 23 | n27DvtZizNEbhz3wRUPQMiQjtqdvAJ9rS9YdPe5h5o5gHx3mw3BSkOttdYheBBMR 24 | AgAeBQJEO0zoAhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEKXTLwEmSaWpVdoA 25 | oLhwWL+E+2I9lrUf4Lf26quOK9vLAKC9ZpIF2tUirFFkBWnQvu13/TA0SokCHAQQ 26 | AQIABgUCTSNBgQAKCRDAc9Iof/uem4NpEACQ8jxmaCaS/qk/Y4GiwLA5bvKosG3B 27 | iARZ2v5UWqCZQ1tS56yKse/lCIzXQqU9BnYW6wOI2rvFf9meLfd8h96peG6oKscs 28 | fbclLDIf68bBvGBQaD0VYFi/Fk/rxmTQBOCQ3AJZs8O5rIM4gPGE0QGvSZ1h7VRw 29 | 3Uyeg4jKXLIeJn2xEmOJgt3auAR2FyKbzHaX9JCoByJZ/eU23akNl9hgt7ePlpXo 30 | 74KNYC58auuMUhCq3BQDB+II4ERYMcmFp1N5ZG05Cl6jcaRRHDXz+Ax6DWprRI1+ 31 | RH/Yyae6LmKpeJNwd+vM14aawnNO9h8IAQ+aJ3oYZdRhGyybbin3giJ10hmWveg/ 32 | Pey91Nh9vBCHdDkdPU0s9zE7z/PHT0c5ccZRukxfZfkrlWQ5iqu3V064ku5f4PBy 33 | 8UPSkETcjYgDnrdnwqIAO+oVg/SFlfsOzftnwUrvwIcZlXAgtP6MEEAs/38e/JIN 34 | g4VrpdAy7HMGEUsh6Ah6lvGQr+zBnG44XwKfl7e0uCYkrAzUJRGM5vx9iXvFMcMu 35 | jv9EBNNBOU8/Y6MBDzGZhgaoeI27nrUvaveJXjAiDKAQWBLjtQjINZ8I9uaSGOul 36 | 8kpbFavE4eS3+KhISrSHe4DuAa3dk9zI+FiPvXY1ZyfQBtNpR+gYFY6VxMbHhY1U 37 | lSLHO2eUIQLdYbRITmV1cm9EZWJpYW4gQXJjaGl2ZSBLZXkgPHBrZy1leHBwc3kt 38 | bWFpbnRhaW5lcnNAbGlzdHMuYWxpb3RoLmRlYmlhbi5vcmc+iEYEEBEIAAYFAk1R 39 | yQYACgkQjRFFY3XAJMgEWwCggx4Gqlcrt76TSMlbU94cESo55AEAoJ3asQEMpe8t 40 | QUX+5aikw3z1AUoCiEoEEBECAAoFAkqf/3cDBQF4AAoJEPd/jbIxRL4PxyMAoKUI 41 | RPWlHCj/+HSFfwhos68wcSwmAKChuC00qutDro+AOo+uuq6YoHXj+ohgBBMRAgAg 42 | BQJKn/8bAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQpdMvASZJpalDggCe 43 | KF9KOgOPdQbFnKXl8KtHory4EEwAnA7jxgorE6kk2QHEXFSF8LzOOH4GiGMEExEC 44 | ACMCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCSp//RgIZAQAKCRCl0y8BJkml 45 | qekFAKCRyt4+FoCzmBbRUUP3Cr8PzH++IgCgkno4vdjsWdyAey8e0KpITTXMFrmJ 46 | AhwEEAECAAYFAk0jQYEACgkQwHPSKH/7npsFfw/+P8B8hpM3+T1fgboBa4R32deu 47 | n8m6b8vZMXwuo/awQtMpzjem8JGXSUQm8iiX4hDtjq6ZoPrlN8T4jNmviBt/F5jI 48 | Jji/PYmhq+Zn9s++mfx+aF4IJrcHJWFkg/6kJzn4oSdl/YlvKf4VRCcQNtj4xV87 49 | GsdamnzU17XapLVMbSaVKh+6Af7ZLDerEH+iAq733HsYaTK+1xKmN7EFVXgS7bZ1 50 | 9C4LTzc97bVHSywpT9yIrg9QQs/1kshfVIHDKyhjF6IwzSVbeGAIL3Oqo5zOMkWv 51 | 7JlEIkkhTyl+FETxNMTMYjAk+Uei3kRodneq3YBF2uFYSEzrXQgHAyn37geiaMYj 52 | h8wu6a85nG1NS0SdxiZDIePmbvD9vWxFZUWYJ/h9ifsLivWcVXlvHoQ0emd+n2ai 53 | FhAck2xsuyHgnGIZMHww5IkQdu/TMqvbcR6d8Xulh+C4Tq7ppy+oTLADSBKII++p 54 | JQioYydRD529EUJgVlhyH27X6YAk3FuRD3zYZRYS2QECiKXvS665o3JRJ0ZSqNgv 55 | YOom8M0zz6bI9grnUoivMI4o7ISpE4ZwffEd37HVzmraaUHDXRhkulFSf1ImtXoj 56 | V9nNSM5p/+9eP7OioTZhSote6Vj6Ja1SZeRkXZK7BwqPbdO0VsYOb7G//ZiOlqs+ 57 | paRr92G/pwBfj5Dq8EK5Ag0ERDtM9RAIAN0EJqBPvLN0tEin/y4Fe0R4n+E+zNXg 58 | bBsq4WidwyUFy3h/6u86FYvegXwUqVS2OsEs5MwPcCVJOfaEthF7I89QJnP9Nfx7 59 | V5I9yFB53o9ii38BN7X+9gSjpfwXOvf/wIDfggxX8/wRFel37GRB7TiiABRArBez 60 | s5x+zTXvT++WPhElySj0uY8bjVR6tso+d65K0UesvAa7PPWeRS+3nhqABSFLuTTT 61 | MMbnVXCGesBrYHlFVXClAYrSIOX8Ub/UnuEYs9+hIV7U4jKzRF9WJhIC1cXHPmOh 62 | vleAf/I9h/0KahD7HLYud40pNBo5tW8jSfp2/Q8TIE0xxshd51/xy4MAAwUH+wWn 63 | zsYVk981OKUEXul8JPyPxbw05fOd6gF4MJ3YodO+6dfoyIl3bewk+11KXZQALKaO 64 | 1xmkAEO1RqizPeetoadBVkQBp5xPudsVElUTOX0pTYhkUd3iBilsCYKK1/KQ9KzD 65 | I+O/lRsm6L9lc6rV0IgPU00P4BAwR+x8Rw7TJFbuS0miR3lP1NSguz+/kpjxzmGP 66 | LyHJ+LVDYFkk6t0jPXhqFdUY6McUTBDEvavTGlVO062l9APTmmSMVFDsPN/rBes2 67 | rYhuuT+lDp+gcaS1UoaYCIm9kKOteQBnowX9V74Z+HKEYLtwILaSnNe6/fNSTvyj 68 | g0z+R+sPCY4nHewbVC+ISQQYEQIACQUCRDtM9QIbDAAKCRCl0y8BJkmlqbecAJ9B 69 | UdSKVg9H+fQNyP5sbOjj4RDtdACfXHrRHa2+XjJP0dhpvJ8IfvYnQsU= 70 | =fAJZ 71 | -----END PGP PUBLIC KEY BLOCK----- 72 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # python cache 2 | __pycache__/**/* 3 | __pycache__ 4 | *.pyc 5 | 6 | # python distribution 7 | build/**/* 8 | build 9 | dist/**/* 10 | dist 11 | dmriprep.egg-info/**/* 12 | dmriprep.egg-info 13 | .eggs/**/* 14 | .eggs 15 | .pytest_cache/**/* 16 | .pytest_cache 17 | .ipynb_checkpoints/**/* 18 | .ipynb_checkpoints 19 | 20 | 21 | # housekeeping tools 22 | .maintenance/**/* 23 | .maintenance 24 | get_version.py 25 | tox.ini 26 | 27 | # pip installs 28 | src/**/* 29 | src/ 30 | 31 | # git 32 | .gitignore 33 | .git/**/* 34 | .git 35 | .github/**/* 36 | .github 37 | .mailmap 38 | 39 | # other 40 | work/**/* 41 | work 42 | out/**/* 43 | out/ 44 | 45 | # CI, etc. 46 | .circleci 47 | .circleci/**/* 48 | .codecov.yml 49 | .coveragerc 50 | .pep8speaks.yml 51 | .travis.yml 52 | .zenodo.json 53 | 54 | .afq 55 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * dmriprep version: 2 | * Python version: 3 | * Operating System: 4 | 5 | ### Description 6 | 7 | Describe what you were trying to get done. 8 | Tell us what happened, what went wrong, and what you expected to happen. 9 | 10 | ### What I Did 11 | 12 | ``` 13 | Paste the command(s) you ran and the output. 14 | If there was a crash, please include the traceback here. 15 | ``` 16 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome 2 | 3 | # Comment to be posted to on PRs from first time contributors in your repository 4 | newPRWelcomeComment: > 5 | Thanks for opening this pull request! 6 | We have detected this is the first time for you to contribute 7 | to *dMRIPrep*. 8 | Please check out our [contributing guidelines](https://github.com/nipreps/dmriprep/blob/master/CONTRIBUTING.md). 9 | 10 | We invite you to list yourself as a *dMRIPrep* contributor, so if your name 11 | is not already mentioned, please modify the 12 | [``.zenodo.json``](https://github.com/nipreps/dmriprep/blob/master/.zenodo.json) 13 | file and insert your data last in the ``contributors`` list. Example: 14 | 15 | ``` 16 | 17 | { 18 | "name": "Contributor, New dMRIPrep", 19 | "affiliation": "Department of dMRI prep'ing, Open Science Made-Up University", 20 | "orcid": "", 21 | "type": "Researcher" 22 | } 23 | 24 | ``` 25 | 26 | 27 | Of course, if you want to opt-out this time there is no 28 | problem at all with adding your name later. 29 | You will be always welcome to add it in the future whenever 30 | you feel it should be listed. 31 | -------------------------------------------------------------------------------- /.github/workflows/pythonpackage.yml: -------------------------------------------------------------------------------- 1 | name: Python package 2 | 3 | on: 4 | push: 5 | branches: [ '*' ] 6 | tags: [ '*' ] 7 | pull_request: 8 | branches: [ master, 'maint/*' ] 9 | 10 | jobs: 11 | build: 12 | if: "!contains(github.event.head_commit.message, '[skip ci]')" 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | python-version: [3.7, 3.8] 17 | pip: ["pip~=20.3", "pip~=21.0"] 18 | 19 | steps: 20 | - name: Install hdf5 libraries 21 | run: | 22 | sudo apt-get install pkg-config libhdf5-103 libhdf5-dev 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v1 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - uses: actions/checkout@v2 28 | with: 29 | ssh-key: "${{ secrets.NIPREPS_DEPLOY }}" 30 | - name: Fetch all tags (for versioneer to work) 31 | if: "!startsWith(github.ref, 'refs/tags/')" 32 | run: | 33 | /usr/bin/git -c protocol.version=2 fetch --tags --prune --unshallow origin 34 | - name: Build in confined, updated environment and interpolate version 35 | run: | 36 | python -m venv /tmp/buildenv 37 | source /tmp/buildenv/bin/activate 38 | python -m pip install -U setuptools pip wheel twine docutils 39 | python setup.py sdist bdist_wheel 40 | python -m twine check dist/dmriprep* 41 | # Interpolate version 42 | if [[ "$GITHUB_REF" == refs/tags/* ]]; then 43 | TAG=${GITHUB_REF##*/} 44 | fi 45 | THISVERSION=$( python get_version.py ) 46 | THISVERSION=${TAG:-$THISVERSION} 47 | echo "Expected VERSION: \"${THISVERSION}\"" 48 | echo "THISVERSION=${THISVERSION}" >> ${GITHUB_ENV} 49 | - name: Install in confined environment [sdist] 50 | run: | 51 | python -m venv /tmp/install_sdist 52 | source /tmp/install_sdist/bin/activate 53 | python -m pip install "${{ matrix.pip }}" setuptools 54 | python -m pip install dist/dmriprep*.tar.gz 55 | INSTALLED_VERSION=$(python -c 'import dmriprep; print(dmriprep.__version__, end="")') 56 | echo "VERSION: \"${THISVERSION}\"" 57 | echo "INSTALLED: \"${INSTALLED_VERSION}\"" 58 | test "${INSTALLED_VERSION}" = "${THISVERSION}" 59 | - name: Install in confined environment [wheel] 60 | run: | 61 | python -m venv /tmp/install_wheel 62 | source /tmp/install_wheel/bin/activate 63 | python -m pip install "${{ matrix.pip }}" setuptools 64 | python -m pip install dist/dmriprep*.whl 65 | INSTALLED_VERSION=$(python -c 'import dmriprep; print(dmriprep.__version__, end="")') 66 | echo "INSTALLED: \"${INSTALLED_VERSION}\"" 67 | test "${INSTALLED_VERSION}" = "${THISVERSION}" 68 | - name: Install in confined environment [setup.py - install] 69 | run: | 70 | python -m venv /tmp/setup_install 71 | source /tmp/setup_install/bin/activate 72 | python -m pip install "${{ matrix.pip }}" setuptools 73 | python -m pip install numpy scipy "Cython >= 0.28.5" # sklearn needs this 74 | python -m pip install scikit-learn # otherwise it attempts to build it 75 | python setup.py install 76 | INSTALLED_VERSION=$(python -c 'import dmriprep; print(dmriprep.__version__, end="")') 77 | echo "INSTALLED: \"${INSTALLED_VERSION}\"" 78 | test "${INSTALLED_VERSION}" = "${THISVERSION}" 79 | - name: Install in confined environment [setup.py - develop] 80 | run: | 81 | python -m venv /tmp/setup_develop 82 | source /tmp/setup_develop/bin/activate 83 | python -m pip install "${{ matrix.pip }}" setuptools 84 | # sklearn needs these dependencies 85 | python -m pip install numpy scipy "Cython >= 0.28.5" 86 | python -m pip install scikit-learn # otherwise it attempts to build it 87 | python setup.py develop 88 | INSTALLED_VERSION=$(python -c 'import dmriprep; print(dmriprep.__version__, end="")') 89 | echo "INSTALLED: \"${INSTALLED_VERSION}\"" 90 | test "${INSTALLED_VERSION}" = "${THISVERSION}" 91 | flake8: 92 | if: "!contains(github.event.head_commit.message, '[skip ci]')" 93 | runs-on: ubuntu-latest 94 | steps: 95 | - uses: actions/checkout@v2 96 | - name: Set up Python 3.7 97 | uses: actions/setup-python@v1 98 | with: 99 | python-version: 3.7 100 | - run: pip install flake8 101 | - run: flake8 dmriprep 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | docs/api/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | .venv 89 | venv/ 90 | ENV/ 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | .spyproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | 99 | # mkdocs documentation 100 | /site 101 | 102 | # mypy 103 | .mypy_cache/ 104 | 105 | # Mac OS nonsense: 106 | .DS_Store 107 | 108 | #kubernetes stuff 109 | kubernetes/jobs/ 110 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Anisha Keshavan 2 | Anisha Keshavan 3 | Ariel Rokem 4 | Ariel Rokem 5 | Derek Pisner 6 | Derek Pisner dPys 7 | Garikoitz Lerma-Usabiaga garikoitz 8 | James D. Kent 9 | James D. Kent 10 | James D. Kent Fred Mertz 11 | Michael Joseph 12 | Michael Joseph mjoseph 13 | Oscar Esteban 14 | Oscar Esteban 15 | Oscar Esteban oesteban 16 | Salim Mansour 17 | Salim Mansour slimnsour <54225067+slimnsour@users.noreply.github.com> 18 | Salim Mansour 19 | -------------------------------------------------------------------------------- /.maint/contributors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "affiliation": "Perelman School of Medicine, University of Pennsylvania, PA, USA", 4 | "name": "Cieslak, Matthew", 5 | "orcid": "0000-0002-1931-4734", 6 | "type": "Researcher" 7 | }, 8 | { 9 | "affiliation": "Neuroscience Program, University of Iowa", 10 | "name": "Kent, James D.", 11 | "orcid": "0000-0002-4892-2659", 12 | "type": "Researcher" 13 | }, 14 | { 15 | "affiliation": "Department of Psychology, Stanford University", 16 | "name": "Poldrack, Russell A.", 17 | "orcid": "0000-0001-6755-0259", 18 | "type": "Researcher" 19 | }, 20 | { 21 | "affiliation": "School of Medicine, New York University", 22 | "name": "Veraart, Jelle", 23 | "orcid": "0000-0003-0781-0420", 24 | "type": "Researcher" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /.maint/developers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "affiliation": "Krembil Centre for Neuroinformatics, The Centre for Addiction and Mental Health, Dept. of Psychiatry, University of Toronto", 4 | "name": "Dickie, Erin W.", 5 | "orcid": "0000-0003-3028-9864" 6 | }, 7 | { 8 | "affiliation": "Dept. of Radiology, Lausanne University Hospital and University of Lausanne", 9 | "name": "Esteban, Oscar", 10 | "orcid": "0000-0001-8435-6191" 11 | }, 12 | { 13 | "affiliation": "The Centre for Addiction and Mental Health", 14 | "name": "Joseph, Michael", 15 | "orcid": "0000-0002-0068-230X" 16 | }, 17 | { 18 | "affiliation": "The University of Washington, eScience Institute", 19 | "name": "Keshavan, Anisha", 20 | "orcid": "0000-0003-3554-043X" 21 | }, 22 | { 23 | "affiliation": [ 24 | "Basque Center on Cognition, Brain and Language (BCBL)", 25 | "Department of Psychology, Stanford University" 26 | ], 27 | "name": "Lerma-Usabiaga, Garikoitz", 28 | "orcid": "0000-0001-9800-4816" 29 | }, 30 | { 31 | "affiliation": "The Centre for Addiction and Mental Health", 32 | "name": "Mansour, Salim", 33 | "orcid": "0000-0002-1092-1650" 34 | }, 35 | { 36 | "affiliation": "Department of Psychology, University of Texas at Austin, TX, USA", 37 | "name": "Pisner, Derek", 38 | "orcid": "0000-0002-1228-0201" 39 | }, 40 | { 41 | "affiliation": "The University of Washington, eScience Institute", 42 | "name": "Richie-Halford, Adam", 43 | "orcid": "0000-0001-9276-9084" 44 | }, 45 | { 46 | "affiliation": "The University of Washington eScience Institute", 47 | "name": "Rokem, Ariel", 48 | "orcid": "0000-0003-0679-1985" 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /.maint/former.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "nrajamani3" 4 | } 5 | ] -------------------------------------------------------------------------------- /.maint/paper_author_list.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Generate an author list for a new paper or abstract.""" 3 | import sys 4 | from pathlib import Path 5 | import json 6 | from update_zenodo import get_git_lines, sort_contributors 7 | 8 | 9 | # These authors should go last 10 | AUTHORS_LAST = ["Rokem, Ariel", "Esteban, Oscar"] 11 | 12 | 13 | def _aslist(inlist): 14 | if not isinstance(inlist, list): 15 | return [inlist] 16 | return inlist 17 | 18 | 19 | if __name__ == "__main__": 20 | devs = json.loads(Path(".maint/developers.json").read_text()) 21 | contribs = json.loads(Path(".maint/contributors.json").read_text()) 22 | 23 | hits, misses = sort_contributors( 24 | devs + contribs, 25 | get_git_lines(), 26 | exclude=json.loads(Path(".maint/former.json").read_text()), 27 | last=AUTHORS_LAST, 28 | ) 29 | # Remove position 30 | affiliations = [] 31 | for item in hits: 32 | del item["position"] 33 | for a in _aslist(item.get("affiliation", "Unaffiliated")): 34 | if a not in affiliations: 35 | affiliations.append(a) 36 | 37 | aff_indexes = [ 38 | ", ".join( 39 | [ 40 | "%d" % (affiliations.index(a) + 1) 41 | for a in _aslist(author.get("affiliation", "Unaffiliated")) 42 | ] 43 | ) 44 | for author in hits 45 | ] 46 | 47 | if misses: 48 | print( 49 | "Some people made commits, but are missing in .maint/ " 50 | f"files: {', '.join(misses)}", 51 | file=sys.stderr, 52 | ) 53 | 54 | print("Authors (%d):" % len(hits)) 55 | print( 56 | "%s." 57 | % "; ".join( 58 | [ 59 | "%s \\ :sup:`%s`\\ " % (i["name"], idx) 60 | for i, idx in zip(hits, aff_indexes) 61 | ] 62 | ) 63 | ) 64 | 65 | print( 66 | "\n\nAffiliations:\n%s" 67 | % "\n".join( 68 | ["{0: >2}. {1}".format(i + 1, a) for i, a in enumerate(affiliations)] 69 | ) 70 | ) 71 | -------------------------------------------------------------------------------- /.maint/update_changes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Collects the pull-requests since the latest release and 4 | # aranges them in the CHANGES.rst.txt file. 5 | # 6 | # This is a script to be run before releasing a new version. 7 | # 8 | # Usage /bin/bash update_changes.sh 1.0.1 9 | # 10 | 11 | # Setting # $ help set 12 | set -u # Treat unset variables as an error when substituting. 13 | set -x # Print command traces before executing command. 14 | 15 | # Check whether the Upcoming release header is present 16 | head -1 CHANGES.rst | grep -q Upcoming 17 | UPCOMING=$? 18 | if [[ "$UPCOMING" == "0" ]]; then 19 | head -n3 CHANGES.rst >> newchanges 20 | fi 21 | 22 | # Elaborate today's release header 23 | HEADER="$1 ($(date '+%B %d, %Y'))" 24 | echo $HEADER >> newchanges 25 | echo $( printf "%${#HEADER}s" | tr " " "=" ) >> newchanges 26 | 27 | # Search for PRs since previous release 28 | git log --grep="Merge pull request" `git describe --tags --abbrev=0`..HEAD --pretty='format: * %b %s' | sed 's/Merge pull request \#\([^\d]*\)\ from\ .*/(\#\1)/' >> newchanges 29 | echo "" >> newchanges 30 | echo "" >> newchanges 31 | 32 | # Add back the Upcoming header if it was present 33 | if [[ "$UPCOMING" == "0" ]]; then 34 | tail -n+4 CHANGES.rst >> newchanges 35 | else 36 | cat CHANGES.rst >> newchanges 37 | fi 38 | 39 | # Replace old CHANGES.rst with new file 40 | mv newchanges CHANGES.rst 41 | 42 | -------------------------------------------------------------------------------- /.maint/update_zenodo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Update and sort the creators list of the zenodo record.""" 3 | import sys 4 | from pathlib import Path 5 | import json 6 | from fuzzywuzzy import fuzz, process 7 | 8 | # These ORCIDs should go last 9 | CREATORS_LAST = ["Rokem, Ariel", "Esteban, Oscar"] 10 | CONTRIBUTORS_LAST = ["Poldrack, Russell A."] 11 | 12 | 13 | def sort_contributors(entries, git_lines, exclude=None, last=None): 14 | """Return a list of author dictionaries, ordered by contribution.""" 15 | last = last or [] 16 | sorted_authors = sorted(entries, key=lambda i: i["name"]) 17 | 18 | first_last = [ 19 | " ".join(val["name"].split(",")[::-1]).strip() for val in sorted_authors 20 | ] 21 | first_last_excl = [ 22 | " ".join(val["name"].split(",")[::-1]).strip() for val in exclude or [] 23 | ] 24 | 25 | unmatched = [] 26 | author_matches = [] 27 | position = 1 28 | for ele in git_lines: 29 | matches = process.extract( 30 | ele, first_last, scorer=fuzz.token_sort_ratio, limit=2 31 | ) 32 | # matches is a list [('First match', % Match), ('Second match', % Match)] 33 | if matches[0][1] > 80: 34 | val = sorted_authors[first_last.index(matches[0][0])] 35 | else: 36 | # skip unmatched names 37 | if ele not in first_last_excl: 38 | unmatched.append(ele) 39 | continue 40 | 41 | if val not in author_matches: 42 | val["position"] = position 43 | author_matches.append(val) 44 | position += 1 45 | 46 | names = {" ".join(val["name"].split(",")[::-1]).strip() for val in author_matches} 47 | for missing_name in first_last: 48 | if missing_name not in names: 49 | missing = sorted_authors[first_last.index(missing_name)] 50 | missing["position"] = position 51 | author_matches.append(missing) 52 | position += 1 53 | 54 | all_names = [val["name"] for val in author_matches] 55 | for last_author in last: 56 | author_matches[all_names.index(last_author)]["position"] = position 57 | position += 1 58 | 59 | author_matches = sorted(author_matches, key=lambda k: k["position"]) 60 | 61 | return author_matches, unmatched 62 | 63 | 64 | def get_git_lines(fname="line-contributors.txt"): 65 | """Run git-line-summary.""" 66 | import shutil 67 | import subprocess as sp 68 | 69 | contrib_file = Path(fname) 70 | 71 | lines = [] 72 | if contrib_file.exists(): 73 | print("WARNING: Reusing existing line-contributors.txt file.", file=sys.stderr) 74 | lines = contrib_file.read_text().splitlines() 75 | 76 | git_line_summary_path = shutil.which("git-line-summary") 77 | if not lines and git_line_summary_path: 78 | print("Running git-line-summary on repo") 79 | lines = sp.check_output([git_line_summary_path]).decode().splitlines() 80 | lines = [l for l in lines if "Not Committed Yet" not in l] 81 | contrib_file.write_text("\n".join(lines)) 82 | 83 | if not lines: 84 | raise RuntimeError( 85 | """\ 86 | Could not find line-contributors from git repository.%s""" 87 | % """ \ 88 | git-line-summary not found, please install git-extras. """ 89 | * (git_line_summary_path is None) 90 | ) 91 | return [" ".join(line.strip().split()[1:-1]) for line in lines if "%" in line] 92 | 93 | 94 | if __name__ == "__main__": 95 | data = get_git_lines() 96 | 97 | zenodo_file = Path(".zenodo.json") 98 | zenodo = json.loads(zenodo_file.read_text()) 99 | 100 | creators = json.loads(Path(".maint/developers.json").read_text()) 101 | zen_creators, miss_creators = sort_contributors( 102 | creators, 103 | data, 104 | exclude=json.loads(Path(".maint/former.json").read_text()), 105 | last=CREATORS_LAST, 106 | ) 107 | contributors = json.loads(Path(".maint/contributors.json").read_text()) 108 | zen_contributors, miss_contributors = sort_contributors( 109 | contributors, 110 | data, 111 | exclude=json.loads(Path(".maint/former.json").read_text()), 112 | last=CONTRIBUTORS_LAST, 113 | ) 114 | zenodo["creators"] = zen_creators 115 | zenodo["contributors"] = zen_contributors 116 | 117 | misses = set(miss_creators).intersection(miss_contributors) 118 | if misses: 119 | print( 120 | "Some people made commits, but are missing in .maint/ " 121 | f"files: {', '.join(misses)}", 122 | file=sys.stderr, 123 | ) 124 | 125 | # Remove position 126 | for creator in zenodo["creators"]: 127 | del creator["position"] 128 | if isinstance(creator["affiliation"], list): 129 | creator["affiliation"] = creator["affiliation"][0] 130 | 131 | for creator in zenodo["contributors"]: 132 | creator["type"] = "Researcher" 133 | del creator["position"] 134 | if isinstance(creator["affiliation"], list): 135 | creator["affiliation"] = creator["affiliation"][0] 136 | 137 | zenodo_file.write_text("%s\n" % json.dumps(zenodo, indent=2)) 138 | -------------------------------------------------------------------------------- /.pep8speaks.yml: -------------------------------------------------------------------------------- 1 | scanner: 2 | diff_only: True # Only show errors caused by the patch 3 | linter: flake8 4 | 5 | message: # Customize the comment made by the bot 6 | opened: # Messages when a new PR is submitted 7 | header: "Hello @{name}, thank you for submitting the Pull Request!" 8 | footer: "To test for issues locally, `pip install flake8` and then run `flake8 dmriprep`." 9 | updated: # Messages when new commits are added to the PR 10 | header: "Hello @{name}, Thank you for updating!" 11 | footer: "To test for issues locally, `pip install flake8` and then run `flake8 dmriprep`." 12 | no_errors: "Cheers! There are no style issues detected in this Pull Request. :beers: " 13 | -------------------------------------------------------------------------------- /.zenodo.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "

dMRIPrep is a robust and easy-to-use pipeline for preprocessing of diverse dMRI data. The transparent workflow dispenses of manual intervention, thereby ensuring the reproducibility of the results.

", 3 | "keywords": [ 4 | "neuroimaging", 5 | "workflow", 6 | "pipeline", 7 | "preprocessing", 8 | "dMRI", 9 | "BIDS" 10 | ], 11 | "license": "Apache-2.0", 12 | "related_identifiers": [ 13 | { 14 | "identifier": "http://dmriprep.org", 15 | "relation": "isPartOf", 16 | "scheme": "url" 17 | }, 18 | { 19 | "identifier": "10.1038/s41592-018-0235-4", 20 | "relation": "isPartOf", 21 | "scheme": "doi" 22 | } 23 | ], 24 | "title": "dMRIPrep: a robust preprocessing pipeline for diffusion MRI", 25 | "upload_type": "software", 26 | "creators": [ 27 | { 28 | "affiliation": "The Centre for Addiction and Mental Health", 29 | "name": "Joseph, Michael", 30 | "orcid": "0000-0002-0068-230X" 31 | }, 32 | { 33 | "affiliation": "The Centre for Addiction and Mental Health", 34 | "name": "Mansour, Salim", 35 | "orcid": "0000-0002-1092-1650" 36 | }, 37 | { 38 | "affiliation": "Department of Psychology, University of Texas at Austin, TX, USA", 39 | "name": "Pisner, Derek", 40 | "orcid": "0000-0002-1228-0201" 41 | }, 42 | { 43 | "affiliation": "The University of Washington, eScience Institute", 44 | "name": "Richie-Halford, Adam", 45 | "orcid": "0000-0001-9276-9084" 46 | }, 47 | { 48 | "affiliation": "Basque Center on Cognition, Brain and Language (BCBL)", 49 | "name": "Lerma-Usabiaga, Garikoitz", 50 | "orcid": "0000-0001-9800-4816" 51 | }, 52 | { 53 | "affiliation": "The University of Washington, eScience Institute", 54 | "name": "Keshavan, Anisha", 55 | "orcid": "0000-0003-3554-043X" 56 | }, 57 | { 58 | "affiliation": "Krembil Centre for Neuroinformatics, The Centre for Addiction and Mental Health, Dept. of Psychiatry, University of Toronto", 59 | "name": "Dickie, Erin W.", 60 | "orcid": "0000-0003-3028-9864" 61 | }, 62 | { 63 | "affiliation": "The University of Washington eScience Institute", 64 | "name": "Rokem, Ariel", 65 | "orcid": "0000-0003-0679-1985" 66 | }, 67 | { 68 | "affiliation": "Dept. of Radiology, Lausanne University Hospital and University of Lausanne", 69 | "name": "Esteban, Oscar", 70 | "orcid": "0000-0001-8435-6191" 71 | } 72 | ], 73 | "contributors": [ 74 | { 75 | "affiliation": "Neuroscience Program, University of Iowa", 76 | "name": "Kent, James D.", 77 | "orcid": "0000-0002-4892-2659", 78 | "type": "Researcher" 79 | }, 80 | { 81 | "affiliation": "Perelman School of Medicine, University of Pennsylvania, PA, USA", 82 | "name": "Cieslak, Matthew", 83 | "orcid": "0000-0002-1931-4734", 84 | "type": "Researcher" 85 | }, 86 | { 87 | "affiliation": "School of Medicine, New York University", 88 | "name": "Veraart, Jelle", 89 | "orcid": "0000-0003-0781-0420", 90 | "type": "Researcher" 91 | }, 92 | { 93 | "affiliation": "Department of Psychology, Stanford University", 94 | "name": "Poldrack, Russell A.", 95 | "orcid": "0000-0001-6755-0259", 96 | "type": "Researcher" 97 | } 98 | ] 99 | } 100 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please check the [contributing guidelines of the NiPreps organization](https://www.nipreps.org/community/CONTRIBUTING) 2 | 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include versioneer.py 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean clean-test clean-pyc clean-build docs help 2 | .DEFAULT_GOAL := help 3 | 4 | define BROWSER_PYSCRIPT 5 | import os, webbrowser, sys 6 | 7 | try: 8 | from urllib import pathname2url 9 | except: 10 | from urllib.request import pathname2url 11 | 12 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) 13 | endef 14 | export BROWSER_PYSCRIPT 15 | 16 | define PRINT_HELP_PYSCRIPT 17 | import re, sys 18 | 19 | for line in sys.stdin: 20 | match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) 21 | if match: 22 | target, help = match.groups() 23 | print("%-20s %s" % (target, help)) 24 | endef 25 | export PRINT_HELP_PYSCRIPT 26 | 27 | BROWSER := python -c "$$BROWSER_PYSCRIPT" 28 | 29 | help: 30 | @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) 31 | 32 | clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts 33 | 34 | clean-build: ## remove build artifacts 35 | rm -fr build/ 36 | rm -fr dist/ 37 | rm -fr .eggs/ 38 | find . -name '*.egg-info' -exec rm -fr {} + 39 | find . -name '*.egg' -exec rm -f {} + 40 | 41 | clean-pyc: ## remove Python file artifacts 42 | find . -name '*.pyc' -exec rm -f {} + 43 | find . -name '*.pyo' -exec rm -f {} + 44 | find . -name '*~' -exec rm -f {} + 45 | find . -name '__pycache__' -exec rm -fr {} + 46 | 47 | clean-test: ## remove test and coverage artifacts 48 | rm -fr .tox/ 49 | rm -f .coverage 50 | rm -fr htmlcov/ 51 | rm -fr .pytest_cache 52 | 53 | lint: ## check style with flake8 54 | flake8 dmriprep tests 55 | 56 | test: ## run tests quickly with the default Python 57 | py.test 58 | 59 | test-all: ## run tests on every Python version with tox 60 | tox 61 | 62 | coverage: ## check code coverage quickly with the default Python 63 | coverage run --source dmriprep -m pytest 64 | coverage report -m 65 | coverage html 66 | $(BROWSER) htmlcov/index.html 67 | 68 | docs: ## generate Sphinx HTML documentation, including API docs 69 | rm -f docs/dmriprep*.rst 70 | rm -f docs/modules.rst 71 | sphinx-apidoc -o docs/ dmriprep 72 | $(MAKE) -C docs clean 73 | $(MAKE) -C docs html 74 | $(BROWSER) docs/_build/html/index.html 75 | 76 | servedocs: docs ## compile the docs watching for changes 77 | watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . 78 | 79 | release: dist ## package and upload a release 80 | twine upload dist/* 81 | 82 | dist: clean ## builds source and wheel package 83 | python setup.py sdist 84 | python setup.py bdist_wheel 85 | ls -l dist 86 | 87 | install: clean ## install the package to the active Python's site-packages 88 | python setup.py install 89 | 90 | docker: 91 | docker build --rm -t nipreps/dmriprep:latest \ 92 | --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ 93 | --build-arg VCS_REF=`git rev-parse --short HEAD` \ 94 | --build-arg VERSION=`python get_version.py` . 95 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | dMRIPrep 2 | Copyright 2021 The NiPreps Developers. 3 | 4 | This product includes software developed by 5 | the NiPreps Community (https://nipreps.org/). 6 | 7 | Portions of this software were developed at the Department of 8 | Psychology at Stanford University, Stanford, CA, US. 9 | 10 | This software redistributes the versioneer Python package, which is 11 | Public domain source code. 12 | 13 | This software is also distributed as a Docker container image. 14 | The bootstraping file for the image ("Dockerfile") is licensed 15 | under the MIT License. 16 | 17 | This software may be distributed through an add-on package called 18 | "Docker Wrapper" that is under the BSD 3-clause License. 19 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | dMRIPrep 3 | ======== 4 | 5 | .. image:: https://img.shields.io/badge/chat-mattermost-blue 6 | :target: https://mattermost.brainhack.org/brainhack/channels/dmriprep 7 | 8 | .. image:: https://img.shields.io/badge/docker-nipreps/dmriprep-brightgreen.svg?logo=docker&style=flat 9 | :target: https://hub.docker.com/r/nipreps/dmriprep/tags/ 10 | 11 | .. image:: https://img.shields.io/pypi/v/dmriprep.svg 12 | :target: https://pypi.python.org/pypi/dmriprep 13 | 14 | .. image:: https://circleci.com/gh/nipreps/dmriprep.svg?style=svg 15 | :target: https://circleci.com/gh/nipreps/dmriprep 16 | 17 | .. image:: https://github.com/nipreps/dmriprep/workflows/Python%20package/badge.svg 18 | :target: https://github.com/nipreps/dmriprep/actions 19 | 20 | .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3392201.svg 21 | :target: https://doi.org/10.5281/zenodo.3392201 22 | 23 | [`Documentation `__] 24 | [`Support at neurostars.org `__] 25 | 26 | About 27 | ----- 28 | The preprocessing of diffusion MRI (dMRI) involves numerous steps to clean and standardize 29 | the data before fitting a particular model. 30 | Generally, researchers create ad-hoc preprocessing workflows for each dataset, 31 | building upon a large inventory of available tools. 32 | The complexity of these workflows has snowballed with rapid advances in 33 | acquisition and processing. 34 | dMRIPrep is an analysis-agnostic tool that addresses the challenge of robust and 35 | reproducible preprocessing for whole-brain dMRI data. 36 | dMRIPrep automatically adapts a best-in-breed workflow to the idiosyncrasies of 37 | virtually any dataset, ensuring high-quality preprocessing without manual intervention. 38 | dMRIPrep equips neuroscientists with an easy-to-use and transparent preprocessing 39 | workflow, which can help ensure the validity of inference and the interpretability 40 | of results. 41 | 42 | The workflow is based on `Nipype `__ and 43 | encompasses a large set of tools from other neuroimaging packages. 44 | This pipeline was designed to provide the best software implementation for each state of 45 | preprocessing, and will be updated as newer and better neuroimaging software 46 | becomes available. 47 | 48 | dMRIPrep performs basic preprocessing steps such as head-motion correction, 49 | susceptibility-derived distortion correction, eddy current correction, etc. 50 | providing outputs that can be easily submitted to a variety of diffusion models. 51 | 52 | Getting involved 53 | ---------------- 54 | We welcome all contributions! 55 | We'd like to ask you to familiarize yourself with our `contributing guidelines `__. 56 | For ideas for contributing to *dMRIPrep*, please see the current list of `issues `__. 57 | For making your contribution, we use the GitHub flow, which is 58 | nicely explained in the chapter `Contributing to a Project `__ in Pro Git 59 | by Scott Chacon and also in the `Making a change section `__ of our guidelines. 60 | If you're still not sure where to begin, feel free to pop into `Mattermost `__ and introduce yourself! 61 | Our project maintainers will do their best to answer any question or concerns and will be happy to help you find somewhere to get started. 62 | 63 | Want to learn more about our future plans for developing `dMRIPrep`? 64 | Please take a look at our `milestones board `__ and `project roadmap `__. 65 | 66 | We ask that all contributors to `dMRIPrep` across all project-related spaces (including but not limited to: GitHub, Mattermost, and project emails), adhere to our `code of conduct `__. 67 | -------------------------------------------------------------------------------- /dmriprep/__about__.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Base module variables.""" 24 | from ._version import get_versions 25 | 26 | __version__ = get_versions()["version"] 27 | del get_versions 28 | 29 | __packagename__ = "dmriprep" 30 | __copyright__ = "Copyright 2019, The dMRIPrep developers" 31 | __credits__ = ( 32 | "Contributors: please check the ``.zenodo.json`` file at the top-level folder" 33 | "of the repository" 34 | ) 35 | __url__ = "https://github.com/nipreps/dmriprep" 36 | 37 | DOWNLOAD_URL = "https://github.com/nipreps/{name}/archive/{ver}.tar.gz".format( 38 | name=__packagename__, ver=__version__ 39 | ) 40 | 41 | __ga_id__ = "UA-156165436-1" 42 | -------------------------------------------------------------------------------- /dmriprep/__init__.py: -------------------------------------------------------------------------------- 1 | """Top-level package for dmriprep.""" 2 | from .__about__ import ( 3 | __version__, 4 | __copyright__, 5 | __credits__, 6 | __packagename__, 7 | ) 8 | 9 | __all__ = [ 10 | "__version__", 11 | "__copyright__", 12 | "__credits__", 13 | "__packagename__", 14 | ] 15 | -------------------------------------------------------------------------------- /dmriprep/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/cli/__init__.py -------------------------------------------------------------------------------- /dmriprep/cli/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 3 | # vi: set ft=python sts=4 ts=4 sw=4 et: 4 | # 5 | # Copyright 2021 The NiPreps Developers 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | # We support and encourage derived works from this project, please read 20 | # about our expectations at 21 | # 22 | # https://www.nipreps.org/community/licensing/ 23 | # 24 | """dMRI preprocessing workflow.""" 25 | from .. import config 26 | 27 | 28 | def main(): 29 | """Entry point.""" 30 | import os 31 | import sys 32 | import gc 33 | from multiprocessing import Process, Manager 34 | from .parser import parse_args 35 | from ..utils.bids import write_derivative_description 36 | 37 | parse_args() 38 | 39 | popylar = None 40 | if not config.execution.notrack: 41 | import popylar 42 | from ..__about__ import __ga_id__ 43 | 44 | config.loggers.cli.info( 45 | "Your usage of dmriprep is being recorded using popylar (https://popylar.github.io/). ", # noqa 46 | "For details, see https://nipreps.github.io/dmriprep/usage.html. ", 47 | "To opt out, call dmriprep with a `--notrack` flag", 48 | ) 49 | popylar.track_event(__ga_id__, "run", "cli_run") 50 | 51 | # CRITICAL Save the config to a file. This is necessary because the execution graph 52 | # is built as a separate process to keep the memory footprint low. The most 53 | # straightforward way to communicate with the child process is via the filesystem. 54 | config_file = config.execution.work_dir / ".dmriprep.toml" 55 | config.to_filename(config_file) 56 | 57 | # CRITICAL Call build_workflow(config_file, retval) in a subprocess. 58 | # Because Python on Linux does not ever free virtual memory (VM), running the 59 | # workflow construction jailed within a process preempts excessive VM buildup. 60 | with Manager() as mgr: 61 | from .workflow import build_workflow 62 | 63 | retval = mgr.dict() 64 | p = Process(target=build_workflow, args=(str(config_file), retval)) 65 | p.start() 66 | p.join() 67 | 68 | retcode = p.exitcode or retval.get("return_code", 0) 69 | dmriprep_wf = retval.get("workflow", None) 70 | 71 | # CRITICAL Load the config from the file. This is necessary because the ``build_workflow`` 72 | # function executed constrained in a process may change the config (and thus the global 73 | # state of dMRIPrep). 74 | config.load(config_file) 75 | 76 | if config.execution.reports_only: 77 | sys.exit(int(retcode > 0)) 78 | 79 | if dmriprep_wf and config.execution.write_graph: 80 | dmriprep_wf.write_graph(graph2use="colored", format="svg", simple_form=True) 81 | 82 | retcode = retcode or (dmriprep_wf is None) * os.EX_SOFTWARE 83 | if retcode != 0: 84 | sys.exit(retcode) 85 | 86 | # Generate boilerplate 87 | with Manager() as mgr: 88 | from .workflow import build_boilerplate 89 | 90 | p = Process(target=build_boilerplate, args=(str(config_file), dmriprep_wf)) 91 | p.start() 92 | p.join() 93 | 94 | if config.execution.boilerplate_only: 95 | sys.exit(int(retcode > 0)) 96 | 97 | # Clean up master process before running workflow, which may create forks 98 | gc.collect() 99 | 100 | if popylar is not None: 101 | popylar.track_event(__ga_id__, "run", "started") 102 | 103 | config.loggers.workflow.log( 104 | 15, 105 | "\n".join( 106 | ["dMRIPrep config:"] + ["\t\t%s" % s for s in config.dumps().splitlines()] 107 | ), 108 | ) 109 | config.loggers.workflow.log(25, "dMRIPrep started!") 110 | errno = 1 # Default is error exit unless otherwise set 111 | try: 112 | dmriprep_wf.run(**config.nipype.get_plugin()) 113 | except Exception as e: 114 | if not config.execution.notrack: 115 | popylar.track_event(__ga_id__, "run", "error") 116 | config.loggers.workflow.critical("dMRIPrep failed: %s", e) 117 | raise 118 | else: 119 | config.loggers.workflow.log(25, "dMRIPrep finished successfully!") 120 | 121 | # Bother users with the boilerplate only iff the workflow went okay. 122 | if (config.execution.output_dir / "dmriprep" / "logs" / "CITATION.md").exists(): 123 | config.loggers.workflow.log( 124 | 25, 125 | "Works derived from this dMRIPrep execution should " 126 | "include the following boilerplate: " 127 | f"{config.execution.output_dir / 'dmriprep' / 'logs' / 'CITATION.md'}.", 128 | ) 129 | 130 | if config.workflow.run_reconall: 131 | from templateflow import api 132 | from niworkflows.utils.misc import _copy_any 133 | 134 | dseg_tsv = str(api.get("fsaverage", suffix="dseg", extension=[".tsv"])) 135 | _copy_any( 136 | dseg_tsv, 137 | str(config.execution.output_dir / "dmriprep" / "desc-aseg_dseg.tsv"), 138 | ) 139 | _copy_any( 140 | dseg_tsv, 141 | str( 142 | config.execution.output_dir / "dmriprep" / "desc-aparcaseg_dseg.tsv" 143 | ), 144 | ) 145 | errno = 0 146 | finally: 147 | from niworkflows.reports import generate_reports 148 | from pkg_resources import resource_filename as pkgrf 149 | 150 | # Generate reports phase 151 | failed_reports = generate_reports( 152 | config.execution.participant_label, 153 | config.execution.output_dir, 154 | config.execution.run_uuid, 155 | config=pkgrf("dmriprep", "config/reports-spec.yml"), 156 | packagename="dmriprep", 157 | ) 158 | write_derivative_description( 159 | config.execution.bids_dir, config.execution.output_dir / "dmriprep" 160 | ) 161 | 162 | if failed_reports and not config.execution.notrack: 163 | popylar.track_event(__ga_id__, "run", "reporting_error") 164 | sys.exit(int((errno + failed_reports) > 0)) 165 | 166 | 167 | if __name__ == "__main__": 168 | raise RuntimeError( 169 | "dmriprep/cli/run.py should not be run directly;\n" 170 | "Please `pip install` dmriprep and use the `dmriprep` command" 171 | ) 172 | -------------------------------------------------------------------------------- /dmriprep/cli/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/cli/tests/__init__.py -------------------------------------------------------------------------------- /dmriprep/cli/tests/test_parser.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Test parser.""" 24 | from packaging.version import Version 25 | import pytest 26 | from ..parser import _build_parser 27 | from .. import version as _version 28 | from ... import config 29 | 30 | MIN_ARGS = ["data/", "out/", "participant"] 31 | 32 | 33 | @pytest.mark.parametrize( 34 | "args,code", 35 | [ 36 | ([], 2), 37 | (MIN_ARGS, 2), # bids_dir does not exist 38 | (MIN_ARGS + ["--fs-license-file"], 2), 39 | (MIN_ARGS + ["--fs-license-file", "fslicense.txt"], 2), 40 | ], 41 | ) 42 | def test_parser_errors(args, code): 43 | """Check behavior of the parser.""" 44 | with pytest.raises(SystemExit) as error: 45 | _build_parser().parse_args(args) 46 | 47 | assert error.value.code == code 48 | 49 | 50 | @pytest.mark.parametrize("args", [MIN_ARGS, MIN_ARGS + ["--fs-license-file"]]) 51 | def test_parser_valid(tmp_path, args): 52 | """Check valid arguments.""" 53 | datapath = (tmp_path / "data").absolute() 54 | datapath.mkdir(exist_ok=True) 55 | args[0] = str(datapath) 56 | 57 | if "--fs-license-file" in args: 58 | _fs_file = tmp_path / "license.txt" 59 | _fs_file.write_text("") 60 | args.insert(args.index("--fs-license-file") + 1, str(_fs_file.absolute())) 61 | 62 | opts = _build_parser().parse_args(args) 63 | 64 | assert opts.bids_dir == datapath 65 | 66 | 67 | @pytest.mark.parametrize( 68 | "argval,gb", 69 | [ 70 | ("1G", 1), 71 | ("1GB", 1), 72 | ("1000", 1), # Default units are MB 73 | ("32000", 32), # Default units are MB 74 | ("4000", 4), # Default units are MB 75 | ("1000M", 1), 76 | ("1000MB", 1), 77 | ("1T", 1000), 78 | ("1TB", 1000), 79 | ("%dK" % 1e6, 1), 80 | ("%dKB" % 1e6, 1), 81 | ("%dB" % 1e9, 1), 82 | ], 83 | ) 84 | def test_memory_arg(tmp_path, argval, gb): 85 | """Check the correct parsing of the memory argument.""" 86 | datapath = (tmp_path / "data").absolute() 87 | datapath.mkdir(exist_ok=True) 88 | _fs_file = tmp_path / "license.txt" 89 | _fs_file.write_text("") 90 | 91 | args = MIN_ARGS + ["--fs-license-file", str(_fs_file)] + ["--mem", argval] 92 | args[0] = str(datapath) 93 | opts = _build_parser().parse_args(args) 94 | 95 | assert opts.memory_gb == gb 96 | 97 | 98 | @pytest.mark.parametrize("current,latest", [("1.0.0", "1.3.2"), ("1.3.2", "1.3.2")]) 99 | def test_get_parser_update(monkeypatch, capsys, current, latest): 100 | """Make sure the out-of-date banner is shown.""" 101 | expectation = Version(current) < Version(latest) 102 | 103 | def _mock_check_latest(*args, **kwargs): 104 | return Version(latest) 105 | 106 | monkeypatch.setattr(config.environment, "version", current) 107 | monkeypatch.setattr(_version, "check_latest", _mock_check_latest) 108 | 109 | _build_parser() 110 | captured = capsys.readouterr().err 111 | 112 | msg = """\ 113 | You are using dMRIPrep-%s, and a newer version of dMRIPrep is available: %s. 114 | Please check out our documentation about how and when to upgrade: 115 | https://dmriprep.readthedocs.io/en/latest/faq.html#upgrading""" % ( 116 | current, 117 | latest, 118 | ) 119 | 120 | assert (msg in captured) is expectation 121 | 122 | 123 | @pytest.mark.parametrize( 124 | "flagged", [(True, None), (True, "random reason"), (False, None)] 125 | ) 126 | def test_get_parser_blacklist(monkeypatch, capsys, flagged): 127 | """Make sure the blacklisting banner is shown.""" 128 | 129 | def _mock_is_bl(*args, **kwargs): 130 | return flagged 131 | 132 | monkeypatch.setattr(_version, "is_flagged", _mock_is_bl) 133 | 134 | _build_parser() 135 | captured = capsys.readouterr().err 136 | 137 | assert ("FLAGGED" in captured) is flagged[0] 138 | if flagged[0]: 139 | assert (flagged[1] or "reason: unknown") in captured 140 | -------------------------------------------------------------------------------- /dmriprep/cli/tests/test_version.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Test version checks.""" 24 | from datetime import datetime 25 | from pathlib import Path 26 | from packaging.version import Version 27 | import pytest 28 | from .. import version as _version 29 | from ..version import check_latest, DATE_FMT, requests, is_flagged 30 | 31 | 32 | class MockResponse: 33 | """Mocks the requests module so that Pypi is not actually queried.""" 34 | 35 | status_code = 200 36 | _json = { 37 | "releases": {"1.0.0": None, "1.0.1": None, "1.1.0": None, "1.1.1rc1": None} 38 | } 39 | 40 | def __init__(self, code=200, json=None): 41 | """Allow setting different response codes.""" 42 | self.status_code = code 43 | if json is not None: 44 | self._json = json 45 | 46 | def json(self): 47 | """Redefine the response object.""" 48 | return self._json 49 | 50 | 51 | def test_check_latest1(tmpdir, monkeypatch): 52 | """Test latest version check.""" 53 | tmpdir.chdir() 54 | monkeypatch.setenv("HOME", str(tmpdir)) 55 | assert str(Path.home()) == str(tmpdir) 56 | 57 | def mock_get(*args, **kwargs): 58 | return MockResponse() 59 | 60 | monkeypatch.setattr(requests, "get", mock_get) 61 | 62 | # Initially, cache should not exist 63 | cachefile = Path.home() / ".cache" / "dmriprep" / "latest" 64 | assert not cachefile.exists() 65 | 66 | # First check actually fetches from pypi 67 | v = check_latest() 68 | assert cachefile.exists() 69 | assert isinstance(v, Version) 70 | assert v == Version("1.1.0") 71 | assert cachefile.read_text().split("|") == [ 72 | str(v), 73 | datetime.now().strftime(DATE_FMT), 74 | ] 75 | 76 | # Second check - test the cache file is read 77 | cachefile.write_text("|".join(("1.0.0", cachefile.read_text().split("|")[1]))) 78 | v = check_latest() 79 | assert isinstance(v, Version) 80 | assert v == Version("1.0.0") 81 | 82 | # Third check - forced oudating of cache 83 | cachefile.write_text("2.0.0|20180121") 84 | v = check_latest() 85 | assert isinstance(v, Version) 86 | assert v == Version("1.1.0") 87 | 88 | # Mock timeouts 89 | def mock_get(*args, **kwargs): 90 | raise requests.exceptions.Timeout 91 | 92 | monkeypatch.setattr(requests, "get", mock_get) 93 | 94 | cachefile.write_text("|".join(("1.0.0", cachefile.read_text().split("|")[1]))) 95 | v = check_latest() 96 | assert isinstance(v, Version) 97 | assert v == Version("1.0.0") 98 | 99 | cachefile.write_text("2.0.0|20180121") 100 | v = check_latest() 101 | assert v is None 102 | 103 | cachefile.unlink() 104 | v = check_latest() 105 | assert v is None 106 | 107 | 108 | @pytest.mark.parametrize( 109 | ("result", "code", "json"), 110 | [ 111 | (None, 404, None), 112 | (None, 200, {"releases": {"1.0.0rc1": None}}), 113 | (Version("1.1.0"), 200, None), 114 | (Version("1.0.0"), 200, {"releases": {"1.0.0": None}}), 115 | ], 116 | ) 117 | def test_check_latest2(tmpdir, monkeypatch, result, code, json): 118 | """Test latest version check with varying server responses.""" 119 | tmpdir.chdir() 120 | monkeypatch.setenv("HOME", str(tmpdir)) 121 | assert str(Path.home()) == str(tmpdir) 122 | 123 | def mock_get(*args, **kwargs): 124 | return MockResponse(code=code, json=json) 125 | 126 | monkeypatch.setattr(requests, "get", mock_get) 127 | 128 | v = check_latest() 129 | if result is None: 130 | assert v is None 131 | else: 132 | assert isinstance(v, Version) 133 | assert v == result 134 | 135 | 136 | @pytest.mark.parametrize( 137 | "bad_cache", 138 | [ 139 | "3laj#r???d|3akajdf#", 140 | "2.0.0|3akajdf#", 141 | "|".join(("2.0.0", datetime.now().strftime(DATE_FMT), "")), 142 | "", 143 | ], 144 | ) 145 | def test_check_latest3(tmpdir, monkeypatch, bad_cache): 146 | """Test latest version check when the cache file is corrupted.""" 147 | tmpdir.chdir() 148 | monkeypatch.setenv("HOME", str(tmpdir)) 149 | assert str(Path.home()) == str(tmpdir) 150 | 151 | def mock_get(*args, **kwargs): 152 | return MockResponse() 153 | 154 | monkeypatch.setattr(requests, "get", mock_get) 155 | 156 | # Initially, cache should not exist 157 | cachefile = Path.home() / ".cache" / "dmriprep" / "latest" 158 | cachefile.parent.mkdir(parents=True, exist_ok=True) 159 | assert not cachefile.exists() 160 | 161 | cachefile.write_text(bad_cache) 162 | v = check_latest() 163 | assert isinstance(v, Version) 164 | assert v == Version("1.1.0") 165 | 166 | 167 | @pytest.mark.parametrize( 168 | ("result", "version", "code", "json"), 169 | [ 170 | (False, "1.2.1", 200, {"flagged": {"1.0.0": None}}), 171 | (True, "1.2.1", 200, {"flagged": {"1.2.1": None}}), 172 | (True, "1.2.1", 200, {"flagged": {"1.2.1": "FATAL Bug!"}}), 173 | (False, "1.2.1", 404, {"flagged": {"1.0.0": None}}), 174 | (False, "1.2.1", 200, {"flagged": []}), 175 | (False, "1.2.1", 200, {}), 176 | ], 177 | ) 178 | def test_is_flagged(monkeypatch, result, version, code, json): 179 | """Test that the flagged-versions check is correct.""" 180 | monkeypatch.setattr(_version, "__version__", version) 181 | 182 | def mock_get(*args, **kwargs): 183 | return MockResponse(code=code, json=json) 184 | 185 | monkeypatch.setattr(requests, "get", mock_get) 186 | 187 | val, reason = is_flagged() 188 | assert val is result 189 | 190 | test_reason = None 191 | if val: 192 | test_reason = json.get("flagged", {}).get(version, None) 193 | 194 | if test_reason is not None: 195 | assert reason == test_reason 196 | else: 197 | assert reason is None 198 | -------------------------------------------------------------------------------- /dmriprep/cli/version.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Version CLI helpers.""" 24 | 25 | from pathlib import Path 26 | from datetime import datetime 27 | import requests 28 | from .. import __version__ 29 | 30 | RELEASE_EXPIRY_DAYS = 14 31 | DATE_FMT = "%Y%m%d" 32 | 33 | 34 | def check_latest(): 35 | """Determine whether this is the latest version.""" 36 | from packaging.version import Version, InvalidVersion 37 | 38 | latest = None 39 | date = None 40 | outdated = None 41 | cachefile = Path.home() / ".cache" / "dmriprep" / "latest" 42 | cachefile.parent.mkdir(parents=True, exist_ok=True) 43 | 44 | try: 45 | latest, date = cachefile.read_text().split("|") 46 | except Exception: 47 | pass 48 | else: 49 | try: 50 | latest = Version(latest) 51 | date = datetime.strptime(date, DATE_FMT) 52 | except (InvalidVersion, ValueError): 53 | latest = None 54 | else: 55 | if abs((datetime.now() - date).days) > RELEASE_EXPIRY_DAYS: 56 | outdated = True 57 | 58 | if latest is None or outdated is True: 59 | try: 60 | response = requests.get( 61 | url="https://pypi.org/pypi/dmriprep/json", timeout=1.0 62 | ) 63 | except Exception: 64 | response = None 65 | 66 | if response and response.status_code == 200: 67 | versions = [Version(rel) for rel in response.json()["releases"].keys()] 68 | versions = [rel for rel in versions if not rel.is_prerelease] 69 | if versions: 70 | latest = sorted(versions)[-1] 71 | else: 72 | latest = None 73 | 74 | if latest is not None: 75 | try: 76 | cachefile.write_text( 77 | "|".join(("%s" % latest, datetime.now().strftime(DATE_FMT))) 78 | ) 79 | except Exception: 80 | pass 81 | 82 | return latest 83 | 84 | 85 | def is_flagged(): 86 | """Check whether current version is flagged.""" 87 | # https://raw.githubusercontent.com/nipreps/dmriprep/master/.versions.json 88 | flagged = tuple() 89 | try: 90 | response = requests.get( 91 | url="""\ 92 | https://raw.githubusercontent.com/nipreps/dmriprep/master/.versions.json""", 93 | timeout=1.0, 94 | ) 95 | except Exception: 96 | response = None 97 | 98 | if response and response.status_code == 200: 99 | flagged = response.json().get("flagged", {}) or {} 100 | 101 | if __version__ in flagged: 102 | return True, flagged[__version__] 103 | 104 | return False, None 105 | -------------------------------------------------------------------------------- /dmriprep/cli/workflow.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """ 24 | The workflow builder factory method. 25 | 26 | All the checks and the construction of the workflow are done 27 | inside this function that has pickleable inputs and output 28 | dictionary (``retval``) to allow isolation using a 29 | ``multiprocessing.Process`` that allows dmriprep to enforce 30 | a hard-limited memory-scope. 31 | 32 | """ 33 | 34 | 35 | def build_workflow(config_file, retval): 36 | """Create the Nipype Workflow that supports the whole execution graph.""" 37 | from niworkflows.utils.bids import collect_participants, check_pipeline_version 38 | from niworkflows.reports import generate_reports 39 | from .. import config 40 | from ..utils.misc import check_deps 41 | from ..workflows.base import init_dmriprep_wf 42 | 43 | config.load(config_file) 44 | build_log = config.loggers.workflow 45 | 46 | output_dir = config.execution.output_dir 47 | version = config.environment.version 48 | 49 | retval["return_code"] = 1 50 | retval["workflow"] = None 51 | 52 | # warn if older results exist: check for dataset_description.json in output folder 53 | msg = check_pipeline_version( 54 | version, output_dir / "dmriprep" / "dataset_description.json" 55 | ) 56 | if msg is not None: 57 | build_log.warning(msg) 58 | 59 | # Please note this is the input folder's dataset_description.json 60 | dset_desc_path = config.execution.bids_dir / "dataset_description.json" 61 | if dset_desc_path.exists(): 62 | from hashlib import sha256 63 | 64 | desc_content = dset_desc_path.read_bytes() 65 | config.execution.bids_description_hash = sha256(desc_content).hexdigest() 66 | 67 | # First check that bids_dir looks like a BIDS folder 68 | subject_list = collect_participants( 69 | config.execution.layout, participant_label=config.execution.participant_label 70 | ) 71 | 72 | # Called with reports only 73 | if config.execution.reports_only: 74 | from pkg_resources import resource_filename as pkgrf 75 | 76 | build_log.log( 77 | 25, "Running --reports-only on participants %s", ", ".join(subject_list) 78 | ) 79 | retval["return_code"] = generate_reports( 80 | subject_list, 81 | config.execution.output_dir, 82 | config.execution.run_uuid, 83 | config=pkgrf("dmriprep", "config/reports-spec.yml"), 84 | packagename="dmriprep", 85 | ) 86 | return retval 87 | 88 | # Build main workflow 89 | INIT_MSG = """ 90 | Running dMRIPrep version {version}: 91 | * BIDS dataset path: {bids_dir}. 92 | * Participant list: {subject_list}. 93 | * Run identifier: {uuid}. 94 | * Output spaces: {spaces}. 95 | """.format 96 | build_log.log( 97 | 25, 98 | INIT_MSG( 99 | version=config.environment.version, 100 | bids_dir=config.execution.bids_dir, 101 | subject_list=subject_list, 102 | uuid=config.execution.run_uuid, 103 | spaces=config.execution.output_spaces, 104 | ), 105 | ) 106 | 107 | retval["workflow"] = init_dmriprep_wf() 108 | 109 | # Check workflow for missing commands 110 | missing = check_deps(retval["workflow"]) 111 | if missing: 112 | build_log.critical( 113 | "Cannot run dMRIPrep. Missing dependencies:%s", 114 | "\n\t* %s".join( 115 | ["{} (Interface: {})".format(cmd, iface) for iface, cmd in missing] 116 | ), 117 | ) 118 | retval["return_code"] = 127 # 127 == command not found. 119 | return retval 120 | 121 | config.to_filename(config_file) 122 | build_log.info( 123 | "dMRIPrep workflow graph with %d nodes built successfully.", 124 | len(retval["workflow"]._get_all_nodes()), 125 | ) 126 | retval["return_code"] = 0 127 | return retval 128 | 129 | 130 | def build_boilerplate(config_file, workflow): 131 | """Write boilerplate in an isolated process.""" 132 | from .. import config 133 | 134 | config.load(config_file) 135 | logs_path = config.execution.output_dir / "dmriprep" / "logs" 136 | boilerplate = workflow.visit_desc() 137 | citation_files = { 138 | ext: logs_path / ("CITATION.%s" % ext) for ext in ("bib", "tex", "md", "html") 139 | } 140 | 141 | if boilerplate: 142 | # To please git-annex users and also to guarantee consistency 143 | # among different renderings of the same file, first remove any 144 | # existing one 145 | for citation_file in citation_files.values(): 146 | try: 147 | citation_file.unlink() 148 | except FileNotFoundError: 149 | pass 150 | 151 | citation_files["md"].write_text(boilerplate) 152 | 153 | if not config.execution.md_only_boilerplate and citation_files["md"].exists(): 154 | from subprocess import check_call, CalledProcessError, TimeoutExpired 155 | from pkg_resources import resource_filename as pkgrf 156 | from shutil import copyfile 157 | 158 | # Generate HTML file resolving citations 159 | cmd = [ 160 | "pandoc", 161 | "-s", 162 | "--bibliography", 163 | pkgrf("dmriprep", "data/boilerplate.bib"), 164 | "--citeproc", 165 | "--metadata", 166 | 'pagetitle="dMRIPrep citation boilerplate"', 167 | str(citation_files["md"]), 168 | "-o", 169 | str(citation_files["html"]), 170 | ] 171 | 172 | config.loggers.cli.info( 173 | "Generating an HTML version of the citation boilerplate..." 174 | ) 175 | try: 176 | check_call(cmd, timeout=10) 177 | except (FileNotFoundError, CalledProcessError, TimeoutExpired): 178 | config.loggers.cli.warning( 179 | "Could not generate CITATION.html file:\n%s", " ".join(cmd) 180 | ) 181 | 182 | # Generate LaTex file resolving citations 183 | cmd = [ 184 | "pandoc", 185 | "-s", 186 | "--bibliography", 187 | pkgrf("dmriprep", "data/boilerplate.bib"), 188 | "--natbib", 189 | str(citation_files["md"]), 190 | "-o", 191 | str(citation_files["tex"]), 192 | ] 193 | config.loggers.cli.info( 194 | "Generating a LaTeX version of the citation boilerplate..." 195 | ) 196 | try: 197 | check_call(cmd, timeout=10) 198 | except (FileNotFoundError, CalledProcessError, TimeoutExpired): 199 | config.loggers.cli.warning( 200 | "Could not generate CITATION.tex file:\n%s", " ".join(cmd) 201 | ) 202 | else: 203 | copyfile(pkgrf("dmriprep", "data/boilerplate.bib"), citation_files["bib"]) 204 | -------------------------------------------------------------------------------- /dmriprep/config/reports-spec.yml: -------------------------------------------------------------------------------- 1 | package: dmriprep 2 | sections: 3 | - name: Summary 4 | reportlets: 5 | - bids: {datatype: figures, desc: summary, suffix: T1w} 6 | - name: Anatomical 7 | reportlets: 8 | - bids: 9 | datatype: figures 10 | desc: conform 11 | extension: [.html] 12 | suffix: T1w 13 | - bids: {datatype: figures, suffix: dseg} 14 | caption: This panel shows the template T1-weighted image (if several T1w images 15 | were found), with contours delineating the detected brain mask and brain tissue 16 | segmentations. 17 | subtitle: Brain mask and brain tissue segmentation of the T1w 18 | - bids: {datatype: figures, space: .*, suffix: T1w, regex_search: True} 19 | caption: Spatial normalization of the T1w image to the {space} template. 20 | description: Results of nonlinear alignment of the T1w reference one or more template 21 | space(s). Hover on the panels with the mouse pointer to transition between both 22 | spaces. 23 | static: false 24 | subtitle: Spatial normalization of the anatomical T1w reference 25 | - bids: {datatype: figures, desc: reconall, suffix: T1w} 26 | caption: Surfaces (white and pial) reconstructed with FreeSurfer (recon-all) 27 | overlaid on the participant's T1w template. 28 | subtitle: Surface reconstruction 29 | - name: B0 field mapping 30 | ordering: session,acquisition,run,fmapid 31 | reportlets: 32 | - bids: {datatype: figures, desc: mapped, suffix: fieldmap} 33 | caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions 34 | along the phase-encoding direction of the image. Some scanners produce a B0 35 | mapping of the field, using Spiral Echo Imaging (SEI) or postprocessing a "phase-difference" 36 | acquisition. The plot below shows an anatomical "magnitude" reference and the corresponding 37 | fieldmap. 38 | description: Hover over the panels with the mouse pointer to also visualize the intensity of the 39 | field inhomogeneity in Hertz. 40 | static: false 41 | subtitle: "Preprocessed B0 mapping acquisition" 42 | - bids: {datatype: figures, desc: phasediff, suffix: fieldmap} 43 | caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions 44 | along the phase-encoding direction of the image. A Gradient-Recalled Echo (GRE) scheme for the 45 | mapping of the B0 inhomogeneities by subtracting the phase maps obtained at 46 | two subsequent echoes. The plot below shows an anatomical "magnitude" reference and the corresponding 47 | fieldmap. 48 | description: Hover over the panels with the mouse pointer to also visualize the intensity of the 49 | field inhomogeneity in Hertz. 50 | static: false 51 | subtitle: "Preprocessed mapping of phase-difference acquisition" 52 | - bids: {datatype: figures, desc: pepolar, suffix: fieldmap} 53 | caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions 54 | along the phase-encoding direction of the image. Utilizing two or more images with different 55 | phase-encoding polarities (PEPolar) or directions, it is possible to estimate the inhomogeneity 56 | of the field. The plot below shows a reference EPI (echo-planar imaging) volume generated 57 | using two or more EPI images with varying phase-encoding blips. 58 | description: Hover on the panels with the mouse pointer to also visualize the intensity of the 59 | inhomogeneity of the field in Hertz. 60 | static: false 61 | subtitle: "Preprocessed estimation with varying Phase-Endocing (PE) blips" 62 | - bids: {datatype: figures, desc: anat, suffix: fieldmap} 63 | caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions 64 | along the phase-encoding direction of the image. Utilizing an anatomically-correct acquisition 65 | (for instance, T1w or T2w), it is possible to estimate the inhomogeneity of the field by means of nonlinear 66 | registration. The plot below shows a reference EPI (echo-planar imaging) volume generated 67 | using two or more EPI images with the same PE encoding, after alignment to the anatomical scan. 68 | description: Hover on the panels with the mouse pointer to also visualize the intensity of the 69 | inhomogeneity of the field in Hertz. 70 | static: false 71 | subtitle: "Preprocessed estimation by nonlinear registration to an anatomical scan (“fieldmap-less”)" 72 | - name: Diffusion 73 | ordering: session,acquisition,run 74 | reportlets: 75 | - bids: {datatype: figures, desc: validation, suffix: dwi} 76 | - bids: {datatype: figures, desc: brain, suffix: mask} 77 | caption: Average b=0 that serves for reference in early preprocessing steps. 78 | descriptions: The reference b=0 is obtained as the voxel-wise median across 79 | all b=0 found in the dataset, after accounting for signal drift. 80 | The red contour shows the brain mask calculated using this reference b=0. 81 | subtitle: Reference b=0 and brain mask 82 | - bids: {datatype: figures, desc: sdc, suffix: dwi} 83 | caption: Susceptibility distortions correction (SDC). 84 | description: The reportlet shows a b=0 reference before and after distortion correction. 85 | static: false 86 | subtitle: Unwarping of susceptibility distortions 87 | - bids: {datatype: figures, desc: eddy, suffix: dwi} 88 | caption: Head-motion and eddy current corrected diffusion data 89 | description: Eddy current-induced geometric distortions and head-motion 90 | realignment parameters were estimated with the joint modeling of 91 | eddy_openmp, included in FSL. 92 | static: false 93 | subtitle: Eddy corrected diffusion data 94 | - bids: {datatype: figures, desc: coreg, suffix: dwi} 95 | caption: Diffusion-weighted data and anatomical data (EPI-space and T1w-space) 96 | were aligned with mri_coreg (FreeSurfer). 97 | WARNING - bbregister refinement rejected. 98 | description: Note that nearest-neighbor interpolation is used in this reportlet 99 | in order to highlight potential slice Inhomogeneities and other artifacts, whereas 100 | the final images are resampled using cubic B-Spline interpolation. 101 | static: false 102 | subtitle: Alignment of diffusion anatomical MRI data (volume based) 103 | - bids: {datatype: figures, desc: bbregister, suffix: dwi} 104 | caption: Diffusion-weighted data and anatomical data (EPI-space and T1w-space) 105 | were aligned with bbregister (FreeSurfer). 106 | description: Note that nearest-neighbor interpolation is used in this reportlet 107 | in order to highlight potential slice Inhomogeneities and other artifacts, whereas 108 | the final images are resampled using cubic B-Spline interpolation. 109 | static: false 110 | subtitle: Alignment of diffusion and anatomical MRI data (surface driven) 111 | - name: About 112 | reportlets: 113 | - bids: {datatype: figures, desc: about, suffix: T1w} 114 | -------------------------------------------------------------------------------- /dmriprep/config/testing.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Utilities and mocks for testing and documentation building.""" 24 | import os 25 | from contextlib import contextmanager 26 | from pathlib import Path 27 | from pkg_resources import resource_filename as pkgrf 28 | from toml import loads 29 | from tempfile import mkdtemp 30 | 31 | 32 | @contextmanager 33 | def mock_config(): 34 | """Create a mock config for documentation and testing purposes.""" 35 | from .. import config 36 | 37 | _old_fs = os.getenv("FREESURFER_HOME") 38 | if not _old_fs: 39 | os.environ["FREESURFER_HOME"] = mkdtemp() 40 | 41 | filename = Path(pkgrf("dmriprep", "data/tests/config.toml")) 42 | settings = loads(filename.read_text()) 43 | for sectionname, configs in settings.items(): 44 | if sectionname != "environment": 45 | section = getattr(config, sectionname) 46 | section.load(configs, init=False) 47 | config.nipype.init() 48 | config.loggers.init() 49 | config.init_spaces() 50 | 51 | config.execution.work_dir = Path(mkdtemp()) 52 | config.execution.bids_dir = Path(pkgrf("dmriprep", "data/tests/THP")).absolute() 53 | config.execution.init() 54 | 55 | yield 56 | 57 | if not _old_fs: 58 | del os.environ["FREESURFER_HOME"] 59 | -------------------------------------------------------------------------------- /dmriprep/conftest.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """py.test configuration.""" 24 | import os 25 | import tempfile 26 | from pathlib import Path 27 | import numpy as np 28 | import nibabel as nb 29 | import pytest 30 | from dipy.data.fetcher import _make_fetcher, UW_RW_URL 31 | 32 | _dipy_datadir_root = os.getenv("DMRIPREP_TESTS_DATA") or Path.home() 33 | dipy_datadir = Path(_dipy_datadir_root) / ".cache" / "data" 34 | dipy_datadir.mkdir(parents=True, exist_ok=True) 35 | 36 | _make_fetcher( 37 | "fetch_sherbrooke_3shell", 38 | str(dipy_datadir), 39 | UW_RW_URL + "1773/38475/", 40 | ["HARDI193.nii.gz", "HARDI193.bval", "HARDI193.bvec"], 41 | ["HARDI193.nii.gz", "HARDI193.bval", "HARDI193.bvec"], 42 | [ 43 | "0b735e8f16695a37bfbd66aab136eb66", 44 | "e9b9bb56252503ea49d31fb30a0ac637", 45 | "0c83f7e8b917cd677ad58a078658ebb7", 46 | ], 47 | doc="Download a 3shell HARDI dataset with 192 gradient direction", 48 | )() 49 | 50 | _sherbrooke_data = { 51 | "dwi_file": dipy_datadir / "HARDI193.nii.gz", 52 | "bvecs": np.loadtxt(dipy_datadir / "HARDI193.bvec").T, 53 | "bvals": np.loadtxt(dipy_datadir / "HARDI193.bval"), 54 | } 55 | 56 | 57 | @pytest.fixture(autouse=True) 58 | def doctest_autoimport(doctest_namespace): 59 | """Make available some fundamental modules to doctest modules.""" 60 | doctest_namespace["np"] = np 61 | doctest_namespace["nb"] = nb 62 | doctest_namespace["os"] = os 63 | doctest_namespace["Path"] = Path 64 | doctest_namespace["data_dir"] = Path(__file__).parent / "data" / "tests" 65 | doctest_namespace["dipy_datadir"] = dipy_datadir 66 | tmpdir = tempfile.TemporaryDirectory() 67 | doctest_namespace["tmpdir"] = tmpdir.name 68 | yield 69 | tmpdir.cleanup() 70 | 71 | 72 | @pytest.fixture() 73 | def dipy_test_data(scope="session"): 74 | """Create a temporal directory shared across tests to pull data in.""" 75 | return _sherbrooke_data 76 | -------------------------------------------------------------------------------- /dmriprep/data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/data/__init__.py -------------------------------------------------------------------------------- /dmriprep/data/flirtsch/b02b0.cnf: -------------------------------------------------------------------------------- 1 | # Resolution (knot-spacing) of warps in mm 2 | --warpres=20,16,14,12,10,6,4,4,4 3 | # Subsampling level (a value of 2 indicates that a 2x2x2 neighbourhood is collapsed to 1 voxel) 4 | --subsamp=2,2,2,2,2,1,1,1,1 5 | # FWHM of gaussian smoothing 6 | --fwhm=8,6,4,3,3,2,1,0,0 7 | # Maximum number of iterations 8 | --miter=5,5,5,5,5,10,10,20,20 9 | # Relative weight of regularisation 10 | --lambda=0.005,0.001,0.0001,0.000015,0.000005,0.0000005,0.00000005,0.0000000005,0.00000000001 11 | # If set to 1 lambda is multiplied by the current average squared difference 12 | --ssqlambda=1 13 | # Regularisation model 14 | --regmod=bending_energy 15 | # If set to 1 movements are estimated along with the field 16 | --estmov=1,1,1,1,1,0,0,0,0 17 | # 0=Levenberg-Marquardt, 1=Scaled Conjugate Gradient 18 | --minmet=0,0,0,0,0,1,1,1,1 19 | # Quadratic or cubic splines 20 | --splineorder=3 21 | # Precision for calculation and storage of Hessian 22 | --numprec=double 23 | # Linear or spline interpolation 24 | --interp=spline 25 | # If set to 1 the images are individually scaled to a common mean intensity 26 | --scale=1 -------------------------------------------------------------------------------- /dmriprep/data/flirtsch/b02b0_1.cnf: -------------------------------------------------------------------------------- 1 | # Resolution (knot-spacing) of warps in mm 2 | --warpres=20,16,14,12,10,6,4,4,4 3 | # Subsampling level (a value of 2 indicates that a 2x2x2 neighbourhood is collapsed to 1 voxel) 4 | --subsamp=1,1,1,1,1,1,1,1,1 5 | # FWHM of gaussian smoothing 6 | --fwhm=8,6,4,3,3,2,1,0,0 7 | # Maximum number of iterations 8 | --miter=5,5,5,5,5,10,10,20,20 9 | # Relative weight of regularisation 10 | --lambda=0.0005,0.0001,0.00001,0.0000015,0.0000005,0.0000005,0.00000005,0.0000000005,0.00000000001 11 | # If set to 1 lambda is multiplied by the current average squared difference 12 | --ssqlambda=1 13 | # Regularisation model 14 | --regmod=bending_energy 15 | # If set to 1 movements are estimated along with the field 16 | --estmov=1,1,1,1,1,0,0,0,0 17 | # 0=Levenberg-Marquardt, 1=Scaled Conjugate Gradient 18 | --minmet=0,0,0,0,0,1,1,1,1 19 | # Quadratic or cubic splines 20 | --splineorder=3 21 | # Precision for calculation and storage of Hessian 22 | --numprec=double 23 | # Linear or spline interpolation 24 | --interp=spline 25 | # If set to 1 the images are individually scaled to a common mean intensity 26 | --scale=1 -------------------------------------------------------------------------------- /dmriprep/data/flirtsch/b02b0_2.cnf: -------------------------------------------------------------------------------- 1 | # Resolution (knot-spacing) of warps in mm 2 | --warpres=20,16,14,12,10,6,4,4,4 3 | # Subsampling level (a value of 2 indicates that a 2x2x2 neighbourhood is collapsed to 1 voxel) 4 | --subsamp=2,2,2,2,2,1,1,1,1 5 | # FWHM of gaussian smoothing 6 | --fwhm=8,6,4,3,3,2,1,0,0 7 | # Maximum number of iterations 8 | --miter=5,5,5,5,5,10,10,20,20 9 | # Relative weight of regularisation 10 | --lambda=0.005,0.001,0.0001,0.000015,0.000005,0.0000005,0.00000005,0.0000000005,0.00000000001 11 | # If set to 1 lambda is multiplied by the current average squared difference 12 | --ssqlambda=1 13 | # Regularisation model 14 | --regmod=bending_energy 15 | # If set to 1 movements are estimated along with the field 16 | --estmov=1,1,1,1,1,0,0,0,0 17 | # 0=Levenberg-Marquardt, 1=Scaled Conjugate Gradient 18 | --minmet=0,0,0,0,0,1,1,1,1 19 | # Quadratic or cubic splines 20 | --splineorder=3 21 | # Precision for calculation and storage of Hessian 22 | --numprec=double 23 | # Linear or spline interpolation 24 | --interp=spline 25 | # If set to 1 the images are individually scaled to a common mean intensity 26 | --scale=1 -------------------------------------------------------------------------------- /dmriprep/data/flirtsch/b02b0_4.cnf: -------------------------------------------------------------------------------- 1 | # Resolution (knot-spacing) of warps in mm 2 | --warpres=20,16,14,12,10,6,4,4,4 3 | # Subsampling level (a value of 2 indicates that a 2x2x2 neighbourhood is collapsed to 1 voxel) 4 | --subsamp=4,4,2,2,2,1,1,1,1 5 | # FWHM of gaussian smoothing 6 | --fwhm=8,6,4,3,3,2,1,0,0 7 | # Maximum number of iterations 8 | --miter=5,5,5,5,5,10,10,20,20 9 | # Relative weight of regularisation 10 | --lambda=0.035,0.006,0.0001,0.000015,0.000005,0.0000005,0.00000005,0.0000000005,0.00000000001 11 | # If set to 1 lambda is multiplied by the current average squared difference 12 | --ssqlambda=1 13 | # Regularisation model 14 | --regmod=bending_energy 15 | # If set to 1 movements are estimated along with the field 16 | --estmov=1,1,1,1,1,0,0,0,0 17 | # 0=Levenberg-Marquardt, 1=Scaled Conjugate Gradient 18 | --minmet=0,0,0,0,0,1,1,1,1 19 | # Quadratic or cubic splines 20 | --splineorder=3 21 | # Precision for calculation and storage of Hessian 22 | --numprec=double 23 | # Linear or spline interpolation 24 | --interp=spline 25 | # If set to 1 the images are individually scaled to a common mean intensity 26 | --scale=1 -------------------------------------------------------------------------------- /dmriprep/data/flirtsch/b02b0_quick.cnf: -------------------------------------------------------------------------------- 1 | # Resolution (knot-spacing) of warps in mm 2 | --warpres=20,14,10 3 | # Subsampling level (a value of 2 indicates that a 2x2x2 neighbourhood is collapsed to 1 voxel) 4 | --subsamp=2,2,2 5 | # FWHM of gaussian smoothing 6 | --fwhm=8,2,1 7 | # Maximum number of iterations 8 | --miter=5,5,5 9 | # Relative weight of regularisation 10 | --lambda=0.005,0.000005,0.0000005 11 | # If set to 1 lambda is multiplied by the current average squared difference 12 | --ssqlambda=1 13 | # Regularisation model 14 | --regmod=bending_energy 15 | # If set to 1 movements are estimated along with the field 16 | --estmov=0,0,0 17 | # 0=Levenberg-Marquardt, 1=Scaled Conjugate Gradient 18 | --minmet=0,0,1 19 | # Quadratic or cubic splines 20 | --splineorder=3 21 | # Precision for calculation and storage of Hessian 22 | --numprec=double 23 | # Linear or spline interpolation 24 | --interp=spline 25 | # If set to 1 the images are individually scaled to a common mean intensity 26 | --scale=1 -------------------------------------------------------------------------------- /dmriprep/data/tests/THP/CHANGES: -------------------------------------------------------------------------------- 1 | 2.0.0 2018-04-09 2 | 3 | - Renamed dataset name to same as OpenfMRI 4 | - Entered authors 5 | 6 | 1.0.0 2018-04-09 7 | 8 | - Initial publish 9 | 10 | 1.0.0 2016-08-31 11 | 12 | - initial release 13 | -------------------------------------------------------------------------------- /dmriprep/data/tests/THP/README: -------------------------------------------------------------------------------- 1 | 2 | This Datasets is from a Traveling Human Phantom (THP) dataset that was 3 | collected for a multi-site neuroimaging reliability study. The THP dataset 4 | includes repeated multi-modal magnetic resonance (MR) images 5 | obtained for five healthy controls performed at eight imaging centers. The 6 | modalities include diffusion weighted images (DWI) and three-dimensional T1 7 | weighted MP-RAGE and T2 SPACE sequences. Intra-subject and inter-site 8 | variability for MRI scans was and can be assessed as repeated images were taken 9 | across multiple imaging protocols. 10 | 11 | Another use for the THP dataset could be to evaluate and improve one's tool 12 | against multi-modal and multi-site data. In addition to the THP dataset, which 13 | is useful as it is, we share our processed results. DWI datasets were processed 14 | to produce common rotationally invariant scalar images and diffusion tensor 15 | images. Anatomical images, T1 and T2, were processed with the BRAINSTools suite 16 | to produce anatomical segmentations. 17 | 18 | @article{magnotta2012multicenter, 19 | abstract = {A number of studies are now collecting diffusion tensor imaging 20 | (DTI) data across sites. While the reliability of anatomical images has been 21 | established by a number of groups, the reliability of DTI data has not been 22 | studied as extensively. In this study, five healthy controls were recruited and 23 | imaged at eight imaging centers. Repeated measures were obtained across two 24 | imaging protocols allowing intra-subject and inter-site variability to be 25 | assessed. Regional measures within white matter were obtained for standard 26 | rotationally invariant measures: fractional anisotropy, mean diffusivity, 27 | radial diffusivity, and axial diffusivity. Intra-subject coefficient of 28 | variation (CV) was typically {\textless}1{\%} for all scalars and regions. 29 | Inter-site CV increased to {\~{}}1{\%}-3{\%}. Inter-vendor variation was 30 | similar to inter-site variability. This variability includes differences in the 31 | actual implementation of the sequence.}, author = {Magnotta, Vincent a. and 32 | Matsui, Joy T. and Liu, Dawei and Johnson, Hans J. and Long, Jeffrey D. and 33 | Bolster, Bradley D. and Mueller, Bryon a. and Lim, Kelvin O. and Mori, Susumu 34 | and Helmer, Karl and Turner, Jessica a. and Lowe, Mark J. and Aylward, 35 | Elizabeth and Flashman, Laura a. and Bonett, Greg and Paulsen, Jane S.}, 36 | doi = {10.1089/brain.2012.0112}, 37 | isbn = {8605457767}, 38 | issn = {2158-0014}, 39 | journal = {Brain Connectivity}, 40 | keywords = {diffusion tensor,fractional anisotropy,magnetic resonance,mean diffusivity,reliability,white matter}, 41 | mendeley-groups = {ReginaThesis,BiosketchReferences,JoysThesis,JoyCrossSectionalDWI,TEMP{\_}HOWES{\_}RE}, 42 | month = {jan}, 43 | number = {6}, 44 | pages = {121018043201009}, 45 | pmid = {23075313}, 46 | publisher = {Mary Ann Liebert, Inc. 140 Huguenot Street, 3rd Floor New Rochelle, NY 10801 USA}, 47 | title = {{Multi-Center Reliability of Diffusion Tensor Imaging}}, 48 | url = {http://www.ncbi.nlm.nih.gov/pubmed/23075313 http://online.liebertpub.com/doi/abs/10.1089/brain.2012.0112 http://www.pubmedcentral.nih.gov/articlerender.fcgi?artid=3623569}, 49 | volume = {2}, 50 | year = {2012} 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /dmriprep/data/tests/THP/dataset_description.json: -------------------------------------------------------------------------------- 1 | {"Name":"DWI Traveling Human Phantom Study","BIDSVersion":"1.0.0","License":"PDDL","Authors":["Vincent A. Magnotta","Joy T. Matsui","Dawei Liu","Hans J. Johnson","Jeffrey D. Long","Bradley D. Bolster Jr","Bryon A. Mueller","Kelvin Lim","Susumu Mori","Karl G. Helmer","Jessica A. Turner","Sarah Reading","Mark J. Lowe","Elizabeth Aylward","Laura A. Flashman","Greg Bonett","Jane S. Paulsen"],"HowToAcknowledge":"Please reference the following papers: DOI: 10.1089/brain.2012.0112 V. a. Magnotta, J. T. Matsui, D. Liu, H. J. Johnson, J. D. Long, B. D. Bolster, B. a. Mueller, K. O. Lim, S. Mori, K. Helmer, J. a. Turner, M. J. Lowe, E. Aylward, L. a. Flashman, G. Bonett, and J. S. Paulsen, 'Multi-Center Reliability of Diffusion Tensor Imaging,' Brain Connect., vol. 2, no. 6, p. 121018043201009, Jan. 2012.","Funding":"This work was supported, in part, by awards from CHDI Foundation, Inc.; NIH R01NS050568; NINDS NS40068 Neu- robiological Predictors of Huntington's Disease; and NINDS R01 NS054893 Cognitive and Functional Brain Changes in Preclinical HD. Dr. Turner was supported by NCRR 1 U24 RR021992 from the Functional Imaging Biomedical Infor- matics Network (S.G. Potkin, PI) for her work on this project while at the University of California, Irvine. This publication was supported by the National Center for Research Resour- ces and the National Center for Advancing Translational Sciences, National Institutes of Health, through Grant UL1RR024979. The content is solely the responsibility of the authors and does not necessarily represent the official views of the NIH.","ReferencesAndLinks":["http://online.liebertpub.com/doi/abs/10.1089/brain.2012.0112"],"DatasetDOI":"10.1089/brain.2012.0112"} -------------------------------------------------------------------------------- /dmriprep/data/tests/THP/sub-THP0005/anat/sub-THP0005_T1w.json: -------------------------------------------------------------------------------- 1 | { 2 | "Manufacturer": "Siemens", 3 | "ManufacturersModelName": "TrioTim", 4 | "MagneticFieldStrength": 3, 5 | "FlipAngle": 10, 6 | "EchoTime": 0.00287, 7 | "RepetitionTime": 2.3, 8 | "PhaseEncodingDirection": "i-", 9 | "PulseSequenceType": "MPRAGE" 10 | } 11 | -------------------------------------------------------------------------------- /dmriprep/data/tests/THP/sub-THP0005/anat/sub-THP0005_T1w.nii.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/data/tests/THP/sub-THP0005/anat/sub-THP0005_T1w.nii.gz -------------------------------------------------------------------------------- /dmriprep/data/tests/THP/sub-THP0005/dwi/sub-THP0005_dwi.bval: -------------------------------------------------------------------------------- 1 | 0 1000 1000 1000 1000 1000 1000 1000 1000 1000 0 1000 1000 1000 1000 1000 1000 1000 1000 1000 0 1000 1000 1000 1000 1000 1000 1000 1000 1000 0 1000 1000 1000 1000 1000 1000 1000 1000 1000 0 1000 1000 1000 1000 1000 1000 1000 1000 1000 0 1000 1000 1000 1000 1000 1000 1000 1000 1000 0 1000 1000 1000 1000 1000 1000 1000 1000 1000 0 1000 1000 1000 1000 1000 1000 1000 1000 2 | -------------------------------------------------------------------------------- /dmriprep/data/tests/THP/sub-THP0005/dwi/sub-THP0005_dwi.bvec: -------------------------------------------------------------------------------- 1 | 0 -0.0199009 -0.262434 0.367204 0.236359 -0.0449325 -0.315527 -0.526612 0.642895 0.542084 0 0.342358 0.0678106 -0.246884 -0.503932 -0.701841 0.755633 0.825419 0.710889 0.517213 0 -0.251451 -0.0574736 -0.350699 -0.600613 0.798123 0.899522 -0.939846 0.826922 -0.635163 0 0.38573 0.0948878 -0.207598 0.49852 -0.738576 0.909626 -0.987418 0.994253 0.919405 0 0.760423 -0.535221 0.26299 -0.030104 -0.326083 -0.603167 0.813876 0.945285 0.944655 0 0.885851 0.747888 0.499364 0.210847 -0.0951955 -0.397256 -0.631246 -0.79977 0.886317 0 0.748609 0.611689 0.346619 0.051539 -0.26252 -0.495958 -0.663635 0.582923 0.483359 0 0.195901 -0.124964 -0.357696 -0.51251 0.311839 -0.0108884 -0.230456 0.0969498 2 | 0 0.227953 0.0411667 0.128116 0.387225 0.51128 0.369998 0.158418 0.101131 0.379396 0 0.610053 0.72487 0.682451 0.529164 0.311968 -0.00582446 0.204151 0.476931 0.701277 0 -0.849933 0.888872 0.826144 0.673745 -0.455025 -0.162651 -0.240951 0.508834 -0.740084 0 0.902228 0.97837 0.962558 -0.852274 0.659841 -0.398347 0.107129 0.100311 0.390402 0 0.648249 -0.841269 0.959954 0.993974 0.939732 0.788061 -0.562252 -0.282198 0.0220326 0 0.311702 0.581618 0.787066 0.897366 0.914852 0.836619 0.654782 0.414268 -0.128519 0 0.267987 0.548221 0.698918 0.765672 0.737689 0.554665 0.302305 0.0822329 0.395239 0 0.51915 0.561368 0.362633 0.0790227 0.206812 0.31362 0.0892421 0.0215161 3 | 0 0.973469 0.964071 0.921275 0.891174 0.858239 0.87381 0.835215 0.759248 0.749803 0 0.714582 0.68554 0.687974 0.68267 0.640386 -0.654969 0.526314 0.516888 0.490614 0 -0.463018 0.454537 0.441017 0.430502 -0.394907 -0.405469 -0.242142 0.239349 -0.221008 0 0.192865 0.183815 0.174315 -0.15845 0.138259 -0.117899 0.116315 -0.0374108 -0.0477532 0 -0.0391043 0.0761872 -0.0965586 -0.105402 -0.102826 -0.123082 0.146554 0.163711 -0.327324 0 -0.343672 -0.319974 -0.362163 -0.387656 -0.392408 -0.377168 -0.41568 -0.434453 0.444887 0 -0.606438 -0.570342 -0.625595 -0.641164 -0.622011 -0.668111 -0.684252 -0.808356 -0.781121 0 -0.831929 -0.818077 -0.860553 -0.855037 -0.927354 -0.949486 -0.968982 -0.995057 4 | -------------------------------------------------------------------------------- /dmriprep/data/tests/THP/sub-THP0005/dwi/sub-THP0005_dwi.json: -------------------------------------------------------------------------------- 1 | { 2 | "Manufacturer": "Siemens", 3 | "ManufacturersModelName": "TrioTim", 4 | "MagneticFieldStrength": 3, 5 | "FlipAngle": 90, 6 | "EchoTime": 0.092, 7 | "RepetitionTime": 10, 8 | "EffectiveEchoSpacing": 0.000699982, 9 | "SliceTiming": [ 10 | 5.02, 11 | 0, 12 | 5.22, 13 | 0.2, 14 | 5.4225, 15 | 0.4025, 16 | 5.6225, 17 | 0.6025, 18 | 5.8225, 19 | 0.8025, 20 | 6.025, 21 | 1.005, 22 | 6.225, 23 | 1.205, 24 | 6.425, 25 | 1.405, 26 | 6.6275, 27 | 1.6075, 28 | 6.8275, 29 | 1.8075, 30 | 7.0275, 31 | 2.0075, 32 | 7.2275, 33 | 2.21, 34 | 7.43, 35 | 2.41, 36 | 7.63, 37 | 2.61, 38 | 7.83, 39 | 2.8125, 40 | 8.0325, 41 | 3.0125, 42 | 8.2325, 43 | 3.2125, 44 | 8.4325, 45 | 3.4125, 46 | 8.635, 47 | 3.615, 48 | 8.835, 49 | 3.815, 50 | 9.035, 51 | 4.015, 52 | 9.2375, 53 | 4.2175, 54 | 9.4375, 55 | 4.4175, 56 | 9.6375, 57 | 4.6175, 58 | 9.84, 59 | 4.82 ], 60 | "PhaseEncodingDirection": "j-" 61 | } 62 | -------------------------------------------------------------------------------- /dmriprep/data/tests/THP/sub-THP0005/dwi/sub-THP0005_dwi.nii.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/data/tests/THP/sub-THP0005/dwi/sub-THP0005_dwi.nii.gz -------------------------------------------------------------------------------- /dmriprep/data/tests/bval: -------------------------------------------------------------------------------- 1 | 0 2 | 2500 3 | 2500 4 | 2500 5 | 2500 6 | 2500 7 | 2500 8 | 2500 9 | 2500 10 | 0 11 | 2500 12 | 2500 13 | 2500 14 | 2500 15 | 2500 16 | 2500 17 | 2500 18 | 2500 19 | 0 20 | 2500 21 | 2500 22 | 2500 23 | 2500 24 | 2500 25 | 2500 26 | 2500 27 | 2500 28 | 0 29 | 2500 30 | 2500 31 | 2500 32 | 2500 33 | 2500 34 | 2500 35 | 2500 36 | 2500 37 | 0 38 | 2500 39 | 2500 40 | 2500 41 | 2500 42 | 2500 43 | 2500 44 | 2500 45 | 2500 46 | 0 47 | 2500 48 | 2500 49 | 2500 50 | 2500 51 | 2500 52 | 2500 53 | 2500 54 | 2500 55 | 0 56 | 2500 57 | 2500 58 | 2500 59 | 2500 60 | 2500 61 | 2500 62 | 2500 63 | 2500 64 | 0 65 | 2500 66 | 2500 67 | 2500 68 | 2500 69 | 2500 70 | 1200 71 | 1200 72 | 1200 73 | 0 74 | 1200 75 | 1200 76 | 1200 77 | 1200 78 | 1200 79 | 1200 80 | 1200 81 | 1200 82 | 0 83 | 1200 84 | 1200 85 | 1200 86 | 1200 87 | 1200 88 | 1200 89 | 1200 90 | 1200 91 | 0 92 | 1200 93 | 1200 94 | 1200 95 | 1200 96 | 1200 97 | 1200 98 | 1200 99 | 1200 100 | 0 101 | 1200 102 | 1200 103 | 1200 104 | 1200 105 | 1200 106 | -------------------------------------------------------------------------------- /dmriprep/data/tests/bvec: -------------------------------------------------------------------------------- 1 | 0.000000000000000000e+00 -4.900384695298463239e-02 -7.261448803568887334e-01 6.832152467094113613e-01 -8.447360612148637760e-01 7.303634462442597508e-01 5.099678730359662182e-02 1.799958601428245167e-02 4.441819297579839287e-01 0.000000000000000000e+00 9.895082430076975744e-01 4.698200933497754805e-01 -4.118960093847884307e-01 5.519743337902043301e-01 1.229201892472143037e-01 8.483882264407188734e-01 3.411076364416253948e-01 -3.610025270265337927e-01 0.000000000000000000e+00 4.719905602831905300e-01 8.560779066344169630e-01 -7.969294748620739943e-01 -4.670873535034215651e-01 -1.300449382906106351e-02 -8.824492810552004851e-01 -1.700067153978899667e-02 4.420322695335568985e-01 0.000000000000000000e+00 -3.650675437439669935e-01 -9.770420137099036229e-01 4.061086485971938109e-01 6.271361033032277765e-01 3.539851329366184296e-01 6.582818049316773168e-01 -4.229953470767730539e-01 -2.119635454050159407e-01 0.000000000000000000e+00 -9.115607335641323639e-01 3.110841596493176264e-01 -6.788842600996385368e-01 -1.349580345758559508e-01 6.469877073503395026e-01 -9.040953870952732618e-01 7.572927502328845595e-01 -1.430136584566050473e-01 0.000000000000000000e+00 -2.330642180393236174e-01 -6.638055334638701677e-01 -1.570514427726418805e-01 8.947830414154962497e-01 -5.937515669429782594e-01 5.998632467678286125e-03 -6.652272138969456128e-01 2.770963077032987942e-01 0.000000000000000000e+00 9.622964329590593113e-01 1.330403838856942245e-01 -7.897449535633485063e-01 1.939680948723744580e-01 2.359970500553113526e-01 8.844064781865559333e-01 -4.631901786104944962e-01 -7.000430539717695799e-01 0.000000000000000000e+00 2.000220036306656346e-01 -5.496906112542929712e-01 6.697194463156821431e-01 -2.370055696963325187e-01 -9.603842305537075852e-01 -4.900384695298463239e-02 -7.261448803568887334e-01 6.832152467094113613e-01 0.000000000000000000e+00 -8.447360612148637760e-01 7.303634462442597508e-01 5.099678730359662182e-02 1.799958601428245167e-02 4.441819297579839287e-01 9.895082430076975744e-01 4.698200933497754805e-01 -4.118960093847884307e-01 0.000000000000000000e+00 5.519743337902043301e-01 1.229201892472143037e-01 8.483882264407188734e-01 3.411076364416253948e-01 -3.610025270265337927e-01 4.719905602831905300e-01 8.560779066344169630e-01 -7.969294748620739943e-01 0.000000000000000000e+00 -4.670873535034215651e-01 -1.300449382906106351e-02 -8.824492810552004851e-01 -1.700067153978899667e-02 4.420322695335568985e-01 -3.650675437439669935e-01 -9.770420137099036229e-01 4.061086485971938109e-01 0.000000000000000000e+00 6.271361033032277765e-01 3.539851329366184296e-01 6.582818049316773168e-01 -4.229953470767730539e-01 -2.119635454050159407e-01 2 | 0.000000000000000000e+00 9.190721499957731355e-01 -3.010600674757899498e-01 -2.550803629734990641e-01 5.018431984968777515e-01 6.193081825002695329e-01 -3.899754323216212543e-02 -8.709799676911119670e-01 -4.942024173433424705e-01 0.000000000000000000e+00 8.604419504414760611e-02 8.546727230086341720e-01 -3.998990382376587105e-01 -7.899632675620676947e-01 4.766904900074895868e-01 -1.410645518020534894e-01 7.882487317184774245e-01 5.290037030388820005e-01 0.000000000000000000e+00 -8.499830005099828778e-01 4.810437769756478188e-01 -1.619856649029560725e-01 9.001683472228679678e-03 -9.983449878002262912e-01 3.871971335242206380e-01 5.360211732545235863e-01 6.510475282044017042e-01 0.000000000000000000e+00 5.801073297849339283e-02 4.000172011094794977e-03 9.022413818587901035e-01 -6.141332813846600569e-01 -7.719675780425689915e-01 4.722021457868566530e-01 -3.219964580584419034e-01 7.538703454499151579e-01 0.000000000000000000e+00 1.039499082134536789e-01 -9.472562674852212927e-01 -6.318922715507681032e-01 2.859110954718133701e-01 -2.299956301245410506e-01 -3.970418901292295244e-01 -6.472502105689252883e-01 -2.840271258858449999e-01 0.000000000000000000e+00 -8.942463988289928167e-01 5.308444853453540002e-01 -7.102326392902913454e-01 2.139481238691801068e-01 -7.996654100242131524e-02 -6.878431896271101076e-01 -7.462548895746186073e-01 -2.760959600220594390e-01 0.000000000000000000e+00 -2.680825821549146504e-01 9.702945290911532128e-01 4.048692483457673186e-01 1.929682593317951778e-01 -9.519881002231204148e-01 2.721250702112480435e-01 3.071261011520989848e-01 -6.600405937448113047e-02 0.000000000000000000e+00 -9.281020968462885268e-01 -7.046034198805026794e-01 -7.266955783156729964e-01 -7.220169675981100710e-01 -2.601040624416292024e-01 9.190721499957731355e-01 -3.010600674757899498e-01 -2.550803629734990641e-01 0.000000000000000000e+00 5.018431984968777515e-01 6.193081825002695329e-01 -3.899754323216212543e-02 -8.709799676911119670e-01 -4.942024173433424705e-01 8.604419504414760611e-02 8.546727230086341720e-01 -3.998990382376587105e-01 0.000000000000000000e+00 -7.899632675620676947e-01 4.766904900074895868e-01 -1.410645518020534894e-01 7.882487317184774245e-01 5.290037030388820005e-01 -8.499830005099828778e-01 4.810437769756478188e-01 -1.619856649029560725e-01 0.000000000000000000e+00 9.001683472228679678e-03 -9.983449878002262912e-01 3.871971335242206380e-01 5.360211732545235863e-01 6.510475282044017042e-01 5.801073297849339283e-02 4.000172011094794977e-03 9.022413818587901035e-01 0.000000000000000000e+00 -6.141332813846600569e-01 -7.719675780425689915e-01 4.722021457868566530e-01 -3.219964580584419034e-01 7.538703454499151579e-01 3 | 0.000000000000000000e+00 -3.910306971146325083e-01 -6.181233279071035591e-01 -6.842155618583269927e-01 -1.859419022319108716e-01 -2.881433870114339379e-01 9.979371319409693353e-01 -4.909887073895935483e-01 7.473060845252568196e-01 0.000000000000000000e+00 -1.160596119200130588e-01 2.209154055963837848e-01 8.187932807916061817e-01 -2.669875853659139930e-01 8.704348360514119198e-01 5.102334852414700528e-01 -5.121616124871325448e-01 7.680053760564486964e-01 0.000000000000000000e+00 2.339953201403953131e-01 1.890172013480196367e-01 5.819484998365458406e-01 -8.841653543833503104e-01 -5.601935803287842047e-02 2.671360068500436835e-01 -8.440333399754065269e-01 6.170450459325896864e-01 0.000000000000000000e+00 9.291719127072475271e-01 -2.130091595907978030e-01 -1.450388030704263254e-01 4.791039768456875070e-01 -5.279778253969902302e-01 -5.862509691336821316e-01 -8.469906831537276082e-01 -6.218930435939619139e-01 0.000000000000000000e+00 3.978083025861015587e-01 -7.702083695497574656e-02 -3.739362493037773350e-01 9.487049986110169808e-01 7.269861873936580565e-01 1.580166716383331615e-01 -8.703364500694975303e-02 9.480905469710600997e-01 0.000000000000000000e+00 -3.821052845108224294e-01 5.268456568305114329e-01 6.862247754269577005e-01 3.919049745641056126e-01 8.006649917867435517e-01 -7.258345285890726029e-01 2.400820020079202227e-02 9.203198667401981670e-01 0.000000000000000000e+00 4.601417454897788889e-02 -2.020613349241370538e-01 -4.608511691046882630e-01 9.618417900372382423e-01 1.949975625457021911e-01 -3.791742706252315687e-01 8.313413356918378794e-01 -7.110437305341831271e-01 0.000000000000000000e+00 -3.140345457001449958e-01 4.487474262785045576e-01 1.529359332631333723e-01 -6.500152755384648318e-01 -1.000400240160112253e-01 -3.910306971146325083e-01 -6.181233279071035591e-01 -6.842155618583269927e-01 0.000000000000000000e+00 -1.859419022319108716e-01 -2.881433870114339379e-01 9.979371319409693353e-01 -4.909887073895935483e-01 7.473060845252568196e-01 -1.160596119200130588e-01 2.209154055963837848e-01 8.187932807916061817e-01 0.000000000000000000e+00 -2.669875853659139930e-01 8.704348360514119198e-01 5.102334852414700528e-01 -5.121616124871325448e-01 7.680053760564486964e-01 2.339953201403953131e-01 1.890172013480196367e-01 5.819484998365458406e-01 0.000000000000000000e+00 -8.841653543833503104e-01 -5.601935803287842047e-02 2.671360068500436835e-01 -8.440333399754065269e-01 6.170450459325896864e-01 9.291719127072475271e-01 -2.130091595907978030e-01 -1.450388030704263254e-01 0.000000000000000000e+00 4.791039768456875070e-01 -5.279778253969902302e-01 -5.862509691336821316e-01 -8.469906831537276082e-01 -6.218930435939619139e-01 4 | -------------------------------------------------------------------------------- /dmriprep/data/tests/config.toml: -------------------------------------------------------------------------------- 1 | [environment] 2 | cpu_count = 8 3 | exec_env = "posix" 4 | free_mem = 1.0 5 | overcommit_policy = "heuristic" 6 | overcommit_limit = "50%" 7 | nipype_version = "1.4.2" 8 | templateflow_version = "0.4.2" 9 | version = "0.2.2" 10 | 11 | [execution] 12 | bids_dir = "THP002/" 13 | bids_description_hash = "b07ee615a588acf67967d70f5b2402ffbe477b99a316433fa7fdaff4f9dad5c1" 14 | boilerplate_only = false 15 | debug = false 16 | fs_license_file = "/opt/freesurfer/license.txt" 17 | fs_subjects_dir = "opt/freesurfer/subjects" 18 | layout = "BIDS Layout: .../home/oesteban/Data/THP002 | Subjects: 1 | Sessions: 2 | Runs: 2" 19 | log_dir = "/tmp/dmriprep/logs" 20 | log_level = 40 21 | low_mem = false 22 | md_only_boilerplate = false 23 | notrack = true 24 | output_dir = "/tmp" 25 | output_spaces = "run" 26 | reports_only = false 27 | run_uuid = "20200311-121754_aa0b4fa9-6b60-4a11-af7d-02deb54a823f" 28 | participant_label = [ "THP0005",] 29 | templateflow_home = "/usr/share/templateflow" 30 | work_dir = "work/" 31 | write_graph = false 32 | 33 | [workflow] 34 | anat_only = false 35 | fmap_bspline = false 36 | force_syn = false 37 | hires = true 38 | ignore = [] 39 | longitudinal = false 40 | run_reconall = true 41 | skull_strip_fixed_seed = false 42 | skull_strip_template = "OASIS30ANTs" 43 | spaces = "run" 44 | 45 | [nipype] 46 | crashfile_format = "txt" 47 | get_linked_libs = false 48 | nprocs = 8 49 | omp_nthreads = 8 50 | plugin = "MultiProc" 51 | resource_monitor = false 52 | stop_on_first_crash = false 53 | 54 | [nipype.plugin_args] 55 | maxtasksperchild = 1 56 | raise_insufficient = false 57 | -------------------------------------------------------------------------------- /dmriprep/data/tests/dwi.nii.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/data/tests/dwi.nii.gz -------------------------------------------------------------------------------- /dmriprep/data/tests/dwi.tsv: -------------------------------------------------------------------------------- 1 | # R A S B 2 | 0.00000000 0.00000000 0.00000000 0 3 | 0.04900000 -0.91900000 -0.39100000 2500 4 | 0.72600000 0.30100000 -0.61800000 2500 5 | -0.68300000 0.25500000 -0.68400000 2500 6 | 0.84500000 -0.50200000 -0.18600000 2500 7 | -0.73000000 -0.61900000 -0.28800000 2500 8 | -0.05100000 0.03900000 0.99800000 2500 9 | -0.01800000 0.87100000 -0.49100000 2500 10 | -0.44400000 0.49400000 0.74700000 2500 11 | 0.00000000 0.00000000 0.00000000 0 12 | -0.98900000 -0.08600000 -0.11600000 2500 13 | -0.47000000 -0.85500000 0.22100000 2500 14 | 0.41200000 0.40000000 0.81900000 2500 15 | -0.55200000 0.79000000 -0.26700000 2500 16 | -0.12300000 -0.47700000 0.87100000 2500 17 | -0.84800000 0.14100000 0.51000000 2500 18 | -0.34100000 -0.78800000 -0.51200000 2500 19 | 0.36100000 -0.52900000 0.76800000 2500 20 | 0.00000000 0.00000000 0.00000000 0 21 | -0.47200000 0.85000000 0.23400000 2500 22 | -0.85600000 -0.48100000 0.18900000 2500 23 | 0.79700000 0.16200000 0.58200000 2500 24 | 0.46700000 -0.00900000 -0.88400000 2500 25 | 0.01300000 0.99800000 -0.05600000 2500 26 | 0.88200000 -0.38700000 0.26700000 2500 27 | 0.01700000 -0.53600000 -0.84400000 2500 28 | -0.44200000 -0.65100000 0.61700000 2500 29 | 0.00000000 0.00000000 0.00000000 0 30 | 0.36500000 -0.05800000 0.92900000 2500 31 | 0.97700000 -0.00400000 -0.21300000 2500 32 | -0.40600000 -0.90200000 -0.14500000 2500 33 | -0.62700000 0.61400000 0.47900000 2500 34 | -0.35400000 0.77200000 -0.52800000 2500 35 | -0.65800000 -0.47200000 -0.58600000 2500 36 | 0.42300000 0.32200000 -0.84700000 2500 37 | 0.21200000 -0.75400000 -0.62200000 2500 38 | 0.00000000 0.00000000 0.00000000 0 39 | 0.91200000 -0.10400000 0.39800000 2500 40 | -0.31100000 0.94700000 -0.07700000 2500 41 | 0.67900000 0.63200000 -0.37400000 2500 42 | 0.13500000 -0.28600000 0.94900000 2500 43 | -0.64700000 0.23000000 0.72700000 2500 44 | 0.90400000 0.39700000 0.15800000 2500 45 | -0.75700000 0.64700000 -0.08700000 2500 46 | 0.14300000 0.28400000 0.94800000 2500 47 | 0.00000000 0.00000000 0.00000000 0 48 | 0.23300000 0.89400000 -0.38200000 2500 49 | 0.66400000 -0.53100000 0.52700000 2500 50 | 0.15700000 0.71000000 0.68600000 2500 51 | -0.89500000 -0.21400000 0.39200000 2500 52 | 0.59400000 0.08000000 0.80100000 2500 53 | -0.00600000 0.68800000 -0.72600000 2500 54 | 0.66500000 0.74600000 0.02400000 2500 55 | -0.27700000 0.27600000 0.92000000 2500 56 | 0.00000000 0.00000000 0.00000000 0 57 | -0.96200000 0.26800000 0.04600000 2500 58 | -0.13300000 -0.97000000 -0.20200000 2500 59 | 0.79000000 -0.40500000 -0.46100000 2500 60 | -0.19400000 -0.19300000 0.96200000 2500 61 | -0.23600000 0.95200000 0.19500000 2500 62 | -0.88400000 -0.27200000 -0.37900000 2500 63 | 0.46300000 -0.30700000 0.83100000 2500 64 | 0.70000000 0.06600000 -0.71100000 2500 65 | 0.00000000 0.00000000 0.00000000 0 66 | -0.20000000 0.92800000 -0.31400000 2500 67 | 0.55000000 0.70500000 0.44900000 2500 68 | -0.67000000 0.72700000 0.15300000 2500 69 | 0.23700000 0.72200000 -0.65000000 2500 70 | 0.96000000 0.26000000 -0.10000000 2500 71 | 0.04900000 -0.91900000 -0.39100000 1200 72 | 0.72600000 0.30100000 -0.61800000 1200 73 | -0.68300000 0.25500000 -0.68400000 1200 74 | 0.00000000 0.00000000 0.00000000 0 75 | 0.84500000 -0.50200000 -0.18600000 1200 76 | -0.73000000 -0.61900000 -0.28800000 1200 77 | -0.05100000 0.03900000 0.99800000 1200 78 | -0.01800000 0.87100000 -0.49100000 1200 79 | -0.44400000 0.49400000 0.74700000 1200 80 | -0.98900000 -0.08600000 -0.11600000 1200 81 | -0.47000000 -0.85500000 0.22100000 1200 82 | 0.41200000 0.40000000 0.81900000 1200 83 | 0.00000000 0.00000000 0.00000000 0 84 | -0.55200000 0.79000000 -0.26700000 1200 85 | -0.12300000 -0.47700000 0.87100000 1200 86 | -0.84800000 0.14100000 0.51000000 1200 87 | -0.34100000 -0.78800000 -0.51200000 1200 88 | 0.36100000 -0.52900000 0.76800000 1200 89 | -0.47200000 0.85000000 0.23400000 1200 90 | -0.85600000 -0.48100000 0.18900000 1200 91 | 0.79700000 0.16200000 0.58200000 1200 92 | 0.00000000 0.00000000 0.00000000 0 93 | 0.46700000 -0.00900000 -0.88400000 1200 94 | 0.01300000 0.99800000 -0.05600000 1200 95 | 0.88200000 -0.38700000 0.26700000 1200 96 | 0.01700000 -0.53600000 -0.84400000 1200 97 | -0.44200000 -0.65100000 0.61700000 1200 98 | 0.36500000 -0.05800000 0.92900000 1200 99 | 0.97700000 -0.00400000 -0.21300000 1200 100 | -0.40600000 -0.90200000 -0.14500000 1200 101 | 0.00000000 0.00000000 0.00000000 0 102 | -0.62700000 0.61400000 0.47900000 1200 103 | -0.35400000 0.77200000 -0.52800000 1200 104 | -0.65800000 -0.47200000 -0.58600000 1200 105 | 0.42300000 0.32200000 -0.84700000 1200 106 | 0.21200000 -0.75400000 -0.62200000 1200 107 | -------------------------------------------------------------------------------- /dmriprep/data/tests/dwi_mask.nii.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/data/tests/dwi_mask.nii.gz -------------------------------------------------------------------------------- /dmriprep/interfaces/__init__.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | """Custom Nipype interfaces for dMRIPrep.""" 4 | from nipype.interfaces.base import OutputMultiObject, SimpleInterface 5 | from niworkflows.interfaces.bids import ( 6 | DerivativesDataSink as _DDS, 7 | _BIDSDataGrabberOutputSpec, 8 | _BIDSDataGrabberInputSpec, 9 | LOGGER as _LOGGER, 10 | ) 11 | 12 | 13 | class DerivativesDataSink(_DDS): 14 | """A patched DataSink.""" 15 | 16 | out_path_base = "dmriprep" 17 | 18 | 19 | class BIDSDataGrabberOutputSpec(_BIDSDataGrabberOutputSpec): 20 | dwi = OutputMultiObject(desc="output DWI images") 21 | 22 | 23 | class BIDSDataGrabber(SimpleInterface): 24 | input_spec = _BIDSDataGrabberInputSpec 25 | output_spec = BIDSDataGrabberOutputSpec 26 | _require_dwis = True 27 | 28 | def __init__(self, *args, **kwargs): 29 | anat_only = kwargs.pop("anat_only", False) 30 | super(BIDSDataGrabber, self).__init__(*args, **kwargs) 31 | if anat_only is not None: 32 | self._require_dwis = not anat_only 33 | 34 | def _run_interface(self, runtime): 35 | bids_dict = self.inputs.subject_data 36 | 37 | self._results["out_dict"] = bids_dict 38 | self._results.update(bids_dict) 39 | 40 | if not bids_dict["t1w"]: 41 | raise FileNotFoundError( 42 | "No T1w images found for subject sub-{}".format(self.inputs.subject_id) 43 | ) 44 | 45 | if self._require_dwis and not bids_dict["dwi"]: 46 | raise FileNotFoundError( 47 | "No diffusion weighted images found for subject sub-{}".format( 48 | self.inputs.subject_id 49 | ) 50 | ) 51 | 52 | for imtype in ["dwi", "t2w", "flair", "fmap", "roi"]: 53 | if not bids_dict[imtype]: 54 | _LOGGER.warning( 55 | 'No "%s" images found for sub-%s', imtype, self.inputs.subject_id 56 | ) 57 | 58 | return runtime 59 | -------------------------------------------------------------------------------- /dmriprep/interfaces/images.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Image tools interfaces.""" 24 | from pathlib import Path 25 | 26 | from nipype import logging 27 | from nipype.interfaces.base import ( 28 | BaseInterfaceInputSpec, 29 | File, 30 | SimpleInterface, 31 | TraitedSpec, 32 | traits, 33 | ) 34 | 35 | from dmriprep.utils.images import extract_b0, median, rescale_b0 36 | 37 | LOGGER = logging.getLogger("nipype.interface") 38 | 39 | 40 | class _ExtractB0InputSpec(BaseInterfaceInputSpec): 41 | in_file = File(exists=True, mandatory=True, desc="dwi file") 42 | b0_ixs = traits.List(traits.Int, mandatory=True, desc="Index of b0s") 43 | 44 | 45 | class _ExtractB0OutputSpec(TraitedSpec): 46 | out_file = File(exists=True, desc="output b0 file") 47 | 48 | 49 | class ExtractB0(SimpleInterface): 50 | """ 51 | Extract all b=0 volumes from a dwi series. 52 | 53 | Example 54 | ------- 55 | >>> os.chdir(tmpdir) 56 | >>> extract_b0 = ExtractB0() 57 | >>> extract_b0.inputs.in_file = str(data_dir / 'dwi.nii.gz') 58 | >>> extract_b0.inputs.b0_ixs = [0, 1, 2] 59 | >>> res = extract_b0.run() # doctest: +SKIP 60 | 61 | """ 62 | 63 | input_spec = _ExtractB0InputSpec 64 | output_spec = _ExtractB0OutputSpec 65 | 66 | def _run_interface(self, runtime): 67 | from nipype.utils.filemanip import fname_presuffix 68 | 69 | out_file = fname_presuffix( 70 | self.inputs.in_file, 71 | suffix="_b0", 72 | newpath=str(Path(runtime.cwd).absolute()), 73 | ) 74 | 75 | self._results["out_file"] = extract_b0( 76 | self.inputs.in_file, self.inputs.b0_ixs, out_path=out_file 77 | ) 78 | return runtime 79 | 80 | 81 | class _RescaleB0InputSpec(BaseInterfaceInputSpec): 82 | in_file = File(exists=True, mandatory=True, desc="b0s file") 83 | mask_file = File(exists=True, mandatory=True, desc="mask file") 84 | 85 | 86 | class _RescaleB0OutputSpec(TraitedSpec): 87 | out_ref = File(exists=True, desc="One average b0 file") 88 | out_b0s = File(exists=True, desc="series of rescaled b0 volumes") 89 | signal_drift = traits.List(traits.Float, desc="estimated signal drift factors") 90 | 91 | 92 | class RescaleB0(SimpleInterface): 93 | """ 94 | Rescale the b0 volumes to deal with average signal decay over time. 95 | 96 | Example 97 | ------- 98 | >>> os.chdir(tmpdir) 99 | >>> rescale_b0 = RescaleB0() 100 | >>> rescale_b0.inputs.in_file = str(data_dir / 'dwi.nii.gz') 101 | >>> rescale_b0.inputs.mask_file = str(data_dir / 'dwi_mask.nii.gz') 102 | >>> res = rescale_b0.run() # doctest: +SKIP 103 | 104 | """ 105 | 106 | input_spec = _RescaleB0InputSpec 107 | output_spec = _RescaleB0OutputSpec 108 | 109 | def _run_interface(self, runtime): 110 | from nipype.utils.filemanip import fname_presuffix 111 | 112 | out_b0s = fname_presuffix( 113 | self.inputs.in_file, 114 | suffix="_rescaled", 115 | newpath=str(Path(runtime.cwd).absolute()), 116 | ) 117 | out_ref = fname_presuffix( 118 | self.inputs.in_file, 119 | suffix="_ref", 120 | newpath=str(Path(runtime.cwd).absolute()), 121 | ) 122 | 123 | self._results["out_b0s"], self._results["signal_drift"] = rescale_b0( 124 | self.inputs.in_file, self.inputs.mask_file, out_b0s 125 | ) 126 | self._results["out_ref"] = median(self._results["out_b0s"], out_path=out_ref) 127 | return runtime 128 | -------------------------------------------------------------------------------- /dmriprep/interfaces/reports.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Interfaces to generate reportlets.""" 24 | 25 | import os 26 | import time 27 | 28 | from nipype.interfaces.base import ( 29 | traits, 30 | TraitedSpec, 31 | BaseInterfaceInputSpec, 32 | File, 33 | Directory, 34 | InputMultiObject, 35 | Str, 36 | isdefined, 37 | SimpleInterface, 38 | ) 39 | from nipype.interfaces import freesurfer as fs 40 | 41 | 42 | SUBJECT_TEMPLATE = """\ 43 | \t
    44 | \t\t
  • Subject ID: {subject_id}
  • 45 | \t\t
  • Structural images: {n_t1s:d} T1-weighted {t2w}
  • 46 | \t\t
  • Diffusion Weighted Images: {n_dwi:d}
  • 47 | \t\t
  • Standard output spaces: {std_spaces}
  • 48 | \t\t
  • Non-standard output spaces: {nstd_spaces}
  • 49 | \t\t
  • FreeSurfer reconstruction: {freesurfer_status}
  • 50 | \t
51 | """ 52 | 53 | ABOUT_TEMPLATE = """\t
    54 | \t\t
  • dMRIPrep version: {version}
  • 55 | \t\t
  • dMRIPrep command: {command}
  • 56 | \t\t
  • Date preprocessed: {date}
  • 57 | \t
58 | 59 | """ 60 | 61 | 62 | class SummaryOutputSpec(TraitedSpec): 63 | out_report = File(exists=True, desc="HTML segment containing summary") 64 | 65 | 66 | class SummaryInterface(SimpleInterface): 67 | output_spec = SummaryOutputSpec 68 | 69 | def _run_interface(self, runtime): 70 | segment = self._generate_segment() 71 | fname = os.path.join(runtime.cwd, "report.html") 72 | with open(fname, "w") as fobj: 73 | fobj.write(segment) 74 | self._results["out_report"] = fname 75 | return runtime 76 | 77 | def _generate_segment(self): 78 | raise NotImplementedError 79 | 80 | 81 | class SubjectSummaryInputSpec(BaseInterfaceInputSpec): 82 | t1w = InputMultiObject(File(exists=True), desc="T1w structural images") 83 | t2w = InputMultiObject(File(exists=True), desc="T2w structural images") 84 | subjects_dir = Directory(desc="FreeSurfer subjects directory") 85 | subject_id = Str(desc="Subject ID") 86 | dwi = InputMultiObject( 87 | traits.Either(File(exists=True), traits.List(File(exists=True))), 88 | desc="DWI files", 89 | ) 90 | std_spaces = traits.List(Str, desc="list of standard spaces") 91 | nstd_spaces = traits.List(Str, desc="list of non-standard spaces") 92 | 93 | 94 | class SubjectSummaryOutputSpec(SummaryOutputSpec): 95 | # This exists to ensure that the summary is run prior to the first ReconAll 96 | # call, allowing a determination whether there is a pre-existing directory 97 | subject_id = Str(desc="FreeSurfer subject ID") 98 | 99 | 100 | class SubjectSummary(SummaryInterface): 101 | input_spec = SubjectSummaryInputSpec 102 | output_spec = SubjectSummaryOutputSpec 103 | 104 | def _run_interface(self, runtime): 105 | if isdefined(self.inputs.subject_id): 106 | self._results["subject_id"] = self.inputs.subject_id 107 | return super(SubjectSummary, self)._run_interface(runtime) 108 | 109 | def _generate_segment(self): 110 | if not isdefined(self.inputs.subjects_dir): 111 | freesurfer_status = "Not run" 112 | else: 113 | recon = fs.ReconAll( 114 | subjects_dir=self.inputs.subjects_dir, 115 | subject_id=self.inputs.subject_id, 116 | T1_files=self.inputs.t1w, 117 | flags="-noskullstrip", 118 | ) 119 | if recon.cmdline.startswith("echo"): 120 | freesurfer_status = "Pre-existing directory" 121 | else: 122 | freesurfer_status = "Run by dMRIPrep" 123 | 124 | t2w_seg = "" 125 | if self.inputs.t2w: 126 | t2w_seg = "(+ {:d} T2-weighted)".format(len(self.inputs.t2w)) 127 | 128 | dwi_files = self.inputs.dwi if isdefined(self.inputs.dwi) else [] 129 | dwi_files = [s[0] if isinstance(s, list) else s for s in dwi_files] 130 | 131 | return SUBJECT_TEMPLATE.format( 132 | subject_id=self.inputs.subject_id, 133 | n_t1s=len(self.inputs.t1w), 134 | t2w=t2w_seg, 135 | n_dwi=len(dwi_files), 136 | std_spaces=", ".join(self.inputs.std_spaces), 137 | nstd_spaces=", ".join(self.inputs.nstd_spaces), 138 | freesurfer_status=freesurfer_status, 139 | ) 140 | 141 | 142 | class AboutSummaryInputSpec(BaseInterfaceInputSpec): 143 | version = Str(desc="dMRIPrep version") 144 | command = Str(desc="dMRIPrep command") 145 | # Date not included - update timestamp only if version or command changes 146 | 147 | 148 | class AboutSummary(SummaryInterface): 149 | input_spec = AboutSummaryInputSpec 150 | 151 | def _generate_segment(self): 152 | return ABOUT_TEMPLATE.format( 153 | version=self.inputs.version, 154 | command=self.inputs.command, 155 | date=time.strftime("%Y-%m-%d %H:%M:%S %z"), 156 | ) 157 | -------------------------------------------------------------------------------- /dmriprep/interfaces/vectors.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Handling the gradient table.""" 24 | from pathlib import Path 25 | import numpy as np 26 | from nipype.utils.filemanip import fname_presuffix 27 | from nipype.interfaces.base import ( 28 | SimpleInterface, 29 | BaseInterfaceInputSpec, 30 | TraitedSpec, 31 | File, 32 | traits, 33 | isdefined, 34 | ) 35 | from ..utils.vectors import DiffusionGradientTable, B0_THRESHOLD, BVEC_NORM_EPSILON 36 | 37 | 38 | class _CheckGradientTableInputSpec(BaseInterfaceInputSpec): 39 | dwi_file = File(exists=True, mandatory=True) 40 | in_bvec = File(exists=True, xor=["in_rasb"]) 41 | in_bval = File(exists=True, xor=["in_rasb"]) 42 | in_rasb = File(exists=True, xor=["in_bval", "in_bvec"]) 43 | b0_threshold = traits.Float(B0_THRESHOLD, usedefault=True) 44 | bvec_norm_epsilon = traits.Float(BVEC_NORM_EPSILON, usedefault=True) 45 | b_scale = traits.Bool(True, usedefault=True) 46 | 47 | 48 | class _CheckGradientTableOutputSpec(TraitedSpec): 49 | out_rasb = File(exists=True) 50 | out_bval = File(exists=True) 51 | out_bvec = File(exists=True) 52 | full_sphere = traits.Bool() 53 | pole = traits.Tuple(traits.Float, traits.Float, traits.Float) 54 | b0_ixs = traits.List(traits.Int) 55 | b0_mask = traits.List(traits.Bool) 56 | 57 | 58 | class CheckGradientTable(SimpleInterface): 59 | """ 60 | Ensure the correctness of the gradient table. 61 | 62 | Example 63 | ------- 64 | 65 | >>> os.chdir(tmpdir) 66 | >>> check = CheckGradientTable( 67 | ... dwi_file=str(data_dir / 'dwi.nii.gz'), 68 | ... in_rasb=str(data_dir / 'dwi.tsv')).run() 69 | >>> check.outputs.pole 70 | (0.0, 0.0, 0.0) 71 | >>> check.outputs.full_sphere 72 | True 73 | 74 | >>> check = CheckGradientTable( 75 | ... dwi_file=str(data_dir / 'dwi.nii.gz'), 76 | ... in_bvec=str(data_dir / 'bvec'), 77 | ... in_bval=str(data_dir / 'bval')).run() 78 | >>> check.outputs.pole 79 | (0.0, 0.0, 0.0) 80 | >>> check.outputs.full_sphere 81 | True 82 | >>> newrasb = np.loadtxt(check.outputs.out_rasb, skiprows=1) 83 | >>> oldrasb = np.loadtxt(str(data_dir / 'dwi.tsv'), skiprows=1) 84 | >>> np.allclose(newrasb, oldrasb, rtol=1.e-3) 85 | True 86 | 87 | """ 88 | 89 | input_spec = _CheckGradientTableInputSpec 90 | output_spec = _CheckGradientTableOutputSpec 91 | 92 | def _run_interface(self, runtime): 93 | rasb_file = _undefined(self.inputs, "in_rasb") 94 | 95 | table = DiffusionGradientTable( 96 | dwi_file=self.inputs.dwi_file, 97 | bvecs=_undefined(self.inputs, "in_bvec"), 98 | bvals=_undefined(self.inputs, "in_bval"), 99 | rasb_file=rasb_file, 100 | b_scale=self.inputs.b_scale, 101 | bvec_norm_epsilon=self.inputs.bvec_norm_epsilon, 102 | b0_threshold=self.inputs.b0_threshold, 103 | ) 104 | pole = table.pole 105 | self._results["pole"] = tuple(pole) 106 | self._results["full_sphere"] = np.all(pole == 0.0) 107 | self._results["b0_mask"] = table.b0mask.tolist() 108 | self._results["b0_ixs"] = np.where(table.b0mask)[0].tolist() 109 | 110 | cwd = Path(runtime.cwd).absolute() 111 | if rasb_file is None: 112 | rasb_file = fname_presuffix( 113 | self.inputs.dwi_file, use_ext=False, suffix=".tsv", newpath=str(cwd) 114 | ) 115 | table.to_filename(rasb_file) 116 | self._results["out_rasb"] = rasb_file 117 | table.to_filename("%s/dwi" % cwd, filetype="fsl") 118 | self._results["out_bval"] = str(cwd / "dwi.bval") 119 | self._results["out_bvec"] = str(cwd / "dwi.bvec") 120 | return runtime 121 | 122 | 123 | def _undefined(objekt, name, default=None): 124 | value = getattr(objekt, name) 125 | if not isdefined(value): 126 | return default 127 | return value 128 | -------------------------------------------------------------------------------- /dmriprep/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/utils/__init__.py -------------------------------------------------------------------------------- /dmriprep/utils/bids.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Utilities to handle BIDS inputs.""" 24 | import os 25 | import sys 26 | import json 27 | from pathlib import Path 28 | from bids import BIDSLayout 29 | 30 | 31 | def collect_data(bids_dir, participant_label, bids_validate=True): 32 | """Replacement for niworkflows' version.""" 33 | if isinstance(bids_dir, BIDSLayout): 34 | layout = bids_dir 35 | else: 36 | layout = BIDSLayout(str(bids_dir), validate=bids_validate) 37 | 38 | queries = { 39 | "fmap": {"datatype": "fmap"}, 40 | "dwi": {"datatype": "dwi", "suffix": "dwi"}, 41 | "flair": {"datatype": "anat", "suffix": "FLAIR"}, 42 | "t2w": {"datatype": "anat", "suffix": "T2w"}, 43 | "t1w": {"datatype": "anat", "suffix": "T1w"}, 44 | "roi": {"datatype": "anat", "suffix": "roi"}, 45 | } 46 | 47 | subj_data = { 48 | dtype: sorted( 49 | layout.get( 50 | return_type="file", 51 | subject=participant_label, 52 | extension=["nii", "nii.gz"], 53 | **query 54 | ) 55 | ) 56 | for dtype, query in queries.items() 57 | } 58 | 59 | return subj_data, layout 60 | 61 | 62 | def write_derivative_description(bids_dir, deriv_dir): 63 | from ..__about__ import __version__, __url__, DOWNLOAD_URL 64 | 65 | bids_dir = Path(bids_dir) 66 | deriv_dir = Path(deriv_dir) 67 | desc = { 68 | "Name": "dMRIPrep - dMRI PREProcessing workflow", 69 | "BIDSVersion": "1.1.1", 70 | "PipelineDescription": { 71 | "Name": "dMRIPrep", 72 | "Version": __version__, 73 | "CodeURL": DOWNLOAD_URL, 74 | }, 75 | "CodeURL": __url__, 76 | "HowToAcknowledge": "Please cite https://doi.org/10.5281/zenodo.3392201.", 77 | } 78 | 79 | # Keys that can only be set by environment 80 | if "DMRIPREP_DOCKER_TAG" in os.environ: 81 | desc["DockerHubContainerTag"] = os.environ["DMRIPREP_DOCKER_TAG"] 82 | if "DMRIPREP_SINGULARITY_URL" in os.environ: 83 | singularity_url = os.environ["DMRIPREP_SINGULARITY_URL"] 84 | desc["SingularityContainerURL"] = singularity_url 85 | 86 | singularity_md5 = _get_shub_version(singularity_url) 87 | if singularity_md5 and singularity_md5 is not NotImplemented: 88 | desc["SingularityContainerMD5"] = _get_shub_version(singularity_url) 89 | 90 | # Keys deriving from source dataset 91 | orig_desc = {} 92 | fname = bids_dir / "dataset_description.json" 93 | if fname.exists(): 94 | with fname.open() as fobj: 95 | orig_desc = json.load(fobj) 96 | 97 | if "DatasetDOI" in orig_desc: 98 | desc["SourceDatasetsURLs"] = [ 99 | "https://doi.org/{}".format(orig_desc["DatasetDOI"]) 100 | ] 101 | if "License" in orig_desc: 102 | desc["License"] = orig_desc["License"] 103 | 104 | with (deriv_dir / "dataset_description.json").open("w") as fobj: 105 | json.dump(desc, fobj, indent=4) 106 | 107 | 108 | def validate_input_dir(exec_env, bids_dir, participant_label): 109 | # Ignore issues and warnings that should not influence dMRIPrep 110 | import tempfile 111 | import subprocess 112 | 113 | validator_config_dict = { 114 | "ignore": [ 115 | "EVENTS_COLUMN_ONSET", 116 | "EVENTS_COLUMN_DURATION", 117 | "TSV_EQUAL_ROWS", 118 | "TSV_EMPTY_CELL", 119 | "TSV_IMPROPER_NA", 120 | "INCONSISTENT_SUBJECTS", 121 | "INCONSISTENT_PARAMETERS", 122 | "PARTICIPANT_ID_COLUMN", 123 | "PARTICIPANT_ID_MISMATCH", 124 | "TASK_NAME_MUST_DEFINE", 125 | "PHENOTYPE_SUBJECTS_MISSING", 126 | "STIMULUS_FILE_MISSING", 127 | "BOLD_NOT_4D", 128 | "EVENTS_TSV_MISSING", 129 | "TSV_IMPROPER_NA", 130 | "ACQTIME_FMT", 131 | "Participants age 89 or higher", 132 | "DATASET_DESCRIPTION_JSON_MISSING", 133 | "TASK_NAME_CONTAIN_ILLEGAL_CHARACTER", 134 | "FILENAME_COLUMN", 135 | "WRONG_NEW_LINE", 136 | "MISSING_TSV_COLUMN_CHANNELS", 137 | "MISSING_TSV_COLUMN_IEEG_CHANNELS", 138 | "MISSING_TSV_COLUMN_IEEG_ELECTRODES", 139 | "UNUSED_STIMULUS", 140 | "CHANNELS_COLUMN_SFREQ", 141 | "CHANNELS_COLUMN_LOWCUT", 142 | "CHANNELS_COLUMN_HIGHCUT", 143 | "CHANNELS_COLUMN_NOTCH", 144 | "CUSTOM_COLUMN_WITHOUT_DESCRIPTION", 145 | "ACQTIME_FMT", 146 | "SUSPICIOUSLY_LONG_EVENT_DESIGN", 147 | "SUSPICIOUSLY_SHORT_EVENT_DESIGN", 148 | "MISSING_TSV_COLUMN_EEG_ELECTRODES", 149 | "MISSING_SESSION", 150 | ], 151 | "error": ["NO_T1W"], 152 | "ignoredFiles": ["/dataset_description.json", "/participants.tsv"], 153 | } 154 | # Limit validation only to data from requested participants 155 | if participant_label: 156 | all_subs = set([s.name[4:] for s in bids_dir.glob("sub-*")]) 157 | selected_subs = set( 158 | [s[4:] if s.startswith("sub-") else s for s in participant_label] 159 | ) 160 | bad_labels = selected_subs.difference(all_subs) 161 | if bad_labels: 162 | error_msg = ( 163 | "Data for requested participant(s) label(s) not found. Could " 164 | "not find data for participant(s): %s. Please verify the requested " 165 | "participant labels." 166 | ) 167 | if exec_env == "docker": 168 | error_msg += ( 169 | " This error can be caused by the input data not being " 170 | "accessible inside the docker container. Please make sure all " 171 | "volumes are mounted properly (see https://docs.docker.com/" 172 | "engine/reference/commandline/run/#mount-volume--v---read-only)" 173 | ) 174 | if exec_env == "singularity": 175 | error_msg += ( 176 | " This error can be caused by the input data not being " 177 | "accessible inside the singularity container. Please make sure " 178 | "all paths are mapped properly (see https://www.sylabs.io/" 179 | "guides/3.0/user-guide/bind_paths_and_mounts.html)" 180 | ) 181 | raise RuntimeError(error_msg % ",".join(bad_labels)) 182 | 183 | ignored_subs = all_subs.difference(selected_subs) 184 | if ignored_subs: 185 | for sub in ignored_subs: 186 | validator_config_dict["ignoredFiles"].append("/sub-%s/**" % sub) 187 | with tempfile.NamedTemporaryFile("w+") as temp: 188 | temp.write(json.dumps(validator_config_dict)) 189 | temp.flush() 190 | try: 191 | subprocess.check_call(["bids-validator", bids_dir, "-c", temp.name]) 192 | except FileNotFoundError: 193 | print("bids-validator does not appear to be installed", file=sys.stderr) 194 | 195 | 196 | def _get_shub_version(singularity_url): 197 | return NotImplemented 198 | -------------------------------------------------------------------------------- /dmriprep/utils/images.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Utilities to handle images.""" 24 | import numpy as np 25 | import nibabel as nb 26 | from nipype.utils.filemanip import fname_presuffix 27 | 28 | 29 | def extract_b0(in_file, b0_ixs, out_path=None): 30 | """Extract the *b0* volumes from a DWI dataset.""" 31 | if out_path is None: 32 | out_path = fname_presuffix(in_file, suffix="_b0") 33 | 34 | img = nb.load(in_file) 35 | bzeros = np.squeeze(np.asanyarray(img.dataobj)[..., b0_ixs]) 36 | 37 | hdr = img.header.copy() 38 | hdr.set_data_shape(bzeros.shape) 39 | hdr.set_xyzt_units("mm") 40 | nb.Nifti1Image(bzeros, img.affine, hdr).to_filename(out_path) 41 | return out_path 42 | 43 | 44 | def rescale_b0(in_file, mask_file, out_path=None): 45 | """Rescale the input volumes using the median signal intensity.""" 46 | if out_path is None: 47 | out_path = fname_presuffix(in_file, suffix="_rescaled", use_ext=True) 48 | 49 | img = nb.squeeze_image(nb.load(in_file)) 50 | if img.dataobj.ndim == 3: 51 | return in_file, [1.0] 52 | 53 | mask_data = nb.load(mask_file).get_fdata() > 0 54 | 55 | dtype = img.get_data_dtype() 56 | data = img.get_fdata() 57 | 58 | median_signal = np.median(data[mask_data, ...], axis=0) 59 | # Normalize to the first volume 60 | signal_drift = median_signal[0] / median_signal 61 | data /= signal_drift 62 | 63 | nb.Nifti1Image(data.astype(dtype), img.affine, img.header).to_filename(out_path) 64 | return out_path, signal_drift.tolist() 65 | 66 | 67 | def median(in_file, out_path=None): 68 | """Average a 4D dataset across the last dimension using median.""" 69 | if out_path is None: 70 | out_path = fname_presuffix(in_file, suffix="_b0ref", use_ext=True) 71 | 72 | img = nb.load(in_file) 73 | if img.dataobj.ndim == 3: 74 | return in_file 75 | if img.shape[-1] == 1: 76 | nb.squeeze_image(img).to_filename(out_path) 77 | return out_path 78 | 79 | dtype = img.get_data_dtype() 80 | median_data = np.median(img.get_fdata(), axis=-1) 81 | 82 | nb.Nifti1Image(median_data.astype(dtype), img.affine, img.header).to_filename( 83 | out_path 84 | ) 85 | return out_path 86 | -------------------------------------------------------------------------------- /dmriprep/utils/misc.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Miscellaneous utilities.""" 24 | 25 | 26 | def check_deps(workflow): 27 | """Make sure dependencies are present in this system.""" 28 | from nipype.utils.filemanip import which 29 | 30 | return sorted( 31 | (node.interface.__class__.__name__, node.interface._cmd) 32 | for node in workflow._get_all_nodes() 33 | if ( 34 | hasattr(node.interface, "_cmd") 35 | and which(node.interface._cmd.split()[0]) is None 36 | ) 37 | ) 38 | 39 | 40 | def sub_prefix(subid): 41 | """ 42 | Make sure the subject ID has the sub- prefix. 43 | 44 | Examples 45 | -------- 46 | >>> sub_prefix("sub-01") 47 | 'sub-01' 48 | 49 | >>> sub_prefix("01") 50 | 'sub-01' 51 | 52 | """ 53 | return f"sub-{subid.replace('sub-', '')}" 54 | -------------------------------------------------------------------------------- /dmriprep/utils/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/utils/tests/__init__.py -------------------------------------------------------------------------------- /dmriprep/utils/tests/test_vectors.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Test vector utilities.""" 24 | import pytest 25 | import numpy as np 26 | import nibabel as nb 27 | from dmriprep.utils import vectors as v 28 | from collections import namedtuple 29 | 30 | 31 | def test_corruption(tmpdir, dipy_test_data, monkeypatch): 32 | """Check whether b-value rescaling is operational.""" 33 | tmpdir.chdir() 34 | 35 | bvals = dipy_test_data["bvals"] 36 | bvecs = dipy_test_data["bvecs"] 37 | 38 | dgt = v.DiffusionGradientTable(**dipy_test_data) 39 | affine = dgt.affine.copy() 40 | 41 | # Test vector hemisphere coverage 42 | assert np.all(dgt.pole == [0.0, 0.0, 0.0]) 43 | 44 | dgt.to_filename("dwi.tsv") 45 | dgt = v.DiffusionGradientTable(rasb_file="dwi.tsv") 46 | assert dgt.normalized is False 47 | with pytest.raises(TypeError): 48 | dgt.to_filename("dwi", filetype="fsl") # You can do this iff the affine is set. 49 | 50 | # check accessing obj.affine 51 | dgt = v.DiffusionGradientTable(dwi_file=namedtuple("Affine", ["affine"])(affine)) 52 | assert np.all(dgt.affine == affine) 53 | dgt = v.DiffusionGradientTable(dwi_file=affine) 54 | assert np.all(dgt.affine == affine) 55 | 56 | # Perform various corruption checks using synthetic corrupted bval-bvec. 57 | dgt = v.DiffusionGradientTable() 58 | dgt.bvecs = bvecs 59 | with pytest.raises(ValueError): 60 | dgt.bvals = bvals[:-1] 61 | 62 | dgt = v.DiffusionGradientTable() 63 | dgt.bvals = bvals 64 | with pytest.raises(ValueError): 65 | dgt.bvecs = bvecs[:-1] 66 | 67 | # Missing b0 68 | bval_no_b0 = bvals.copy() 69 | bval_no_b0[0] = 51 70 | with pytest.raises(ValueError): 71 | dgt = v.DiffusionGradientTable( 72 | dwi_file=dipy_test_data["dwi_file"], 73 | bvals=bval_no_b0, 74 | bvecs=bvecs, 75 | raise_inconsistent=True, 76 | ) 77 | bvec_no_b0 = bvecs.copy() 78 | bvec_no_b0[0] = np.array([1.0, 0.0, 0.0]) 79 | with pytest.raises(ValueError): 80 | dgt = v.DiffusionGradientTable( 81 | dwi_file=dipy_test_data["dwi_file"], 82 | bvals=bvals, 83 | bvecs=bvec_no_b0, 84 | raise_inconsistent=True, 85 | ) 86 | 87 | # Corrupt b0 b-val 88 | bval_odd_b0 = bvals.copy() 89 | bval_odd_b0[bval_odd_b0 == 0] = 1e-8 90 | dgt = v.DiffusionGradientTable( 91 | dwi_file=dipy_test_data["dwi_file"], bvals=bval_odd_b0, bvecs=bvecs 92 | ) 93 | assert dgt.bvals[0] == 0 94 | 95 | # Corrupt b0 b-vec 96 | bvec_odd_b0 = bvecs.copy() 97 | b0mask = np.all(bvec_odd_b0 == 0, axis=1) 98 | bvec_odd_b0[b0mask] = [10, 10, 10] 99 | dgt = v.DiffusionGradientTable( 100 | dwi_file=dipy_test_data["dwi_file"], bvals=bvals, bvecs=bvec_odd_b0 101 | ) 102 | assert np.all(dgt.bvecs[b0mask] == [0.0, 0.0, 0.0]) 103 | 104 | # Test normalization 105 | bvecs_factor = 2.0 * bvecs 106 | dgt = v.DiffusionGradientTable( 107 | dwi_file=dipy_test_data["dwi_file"], bvals=bvals, bvecs=bvecs_factor 108 | ) 109 | assert -1.0 <= np.max(np.abs(dgt.gradients[..., :-1])) <= 1.0 110 | assert dgt.normalized is True 111 | 112 | def mock_func(*args, **kwargs): 113 | return "called!" 114 | 115 | with monkeypatch.context() as m: 116 | m.setattr(v, "normalize_gradients", mock_func) 117 | assert dgt.normalize() is None # Test nothing is executed. 118 | 119 | with monkeypatch.context() as m: 120 | m.setattr(v, "bvecs2ras", mock_func) 121 | assert dgt.generate_vecval() is None # Test nothing is executed. 122 | 123 | # Miscellaneous tests 124 | with pytest.raises(ValueError): 125 | dgt.to_filename("path", filetype="mrtrix") 126 | 127 | 128 | def test_b0mask_from_data(tmp_path): 129 | """Check the estimation of bzeros using the dwi data.""" 130 | 131 | highb = np.random.normal(100, 5, size=(40, 40, 40, 99)) 132 | mask_file = tmp_path / "mask.nii.gz" 133 | 134 | # Test 1: no lowb 135 | dwi_file = tmp_path / "only_highb.nii.gz" 136 | nb.Nifti1Image(highb.astype(float), np.eye(4), None).to_filename(dwi_file) 137 | nb.Nifti1Image(np.ones((40, 40, 40), dtype=np.uint8), np.eye(4), None).to_filename( 138 | mask_file 139 | ) 140 | 141 | assert v.b0mask_from_data(dwi_file, mask_file).sum() == 0 142 | 143 | # Test 1: one lowb 144 | lowb = np.random.normal(400, 50, size=(40, 40, 40, 1)) 145 | dwi_file = tmp_path / "dwi.nii.gz" 146 | nb.Nifti1Image( 147 | np.concatenate((lowb, highb), axis=3).astype(float), np.eye(4), None 148 | ).to_filename(dwi_file) 149 | assert v.b0mask_from_data(dwi_file, mask_file).sum() == 1 150 | -------------------------------------------------------------------------------- /dmriprep/workflows/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/workflows/__init__.py -------------------------------------------------------------------------------- /dmriprep/workflows/dwi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/dmriprep/workflows/dwi/__init__.py -------------------------------------------------------------------------------- /dmriprep/workflows/dwi/eddy.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """ 24 | Eddy-currents and head-motion estimation/correction. 25 | 26 | .. testsetup:: 27 | >>> tmpdir = getfixture('tmpdir') 28 | >>> tmp = tmpdir.chdir() # changing to a temporary directory 29 | >>> nb.Nifti1Image(np.zeros((90, 90, 60, 6)), None, None).to_filename( 30 | ... tmpdir.join('dwi.nii.gz').strpath) 31 | 32 | """ 33 | from nipype.pipeline import engine as pe 34 | from nipype.interfaces import utility as niu 35 | from niworkflows.engine.workflows import LiterateWorkflow as Workflow 36 | 37 | 38 | def gen_eddy_textfiles(in_file, in_meta, newpath=None): 39 | """ 40 | Generate the acquisition-parameters and index files for FSL ``eddy_openmp``. 41 | 42 | Examples 43 | -------- 44 | >>> out_acqparams, out_index = gen_eddy_textfiles( 45 | ... "dwi.nii.gz", 46 | ... {"PhaseEncodingDirection": "j-", "TotalReadoutTime": 0.005}, 47 | ... ) 48 | >>> Path(out_acqparams).read_text() 49 | '0 -1 0 0.0050000' 50 | 51 | >>> Path(out_index).read_text() 52 | '1 1 1 1 1 1' 53 | 54 | """ 55 | from pathlib import Path 56 | import nibabel as nb 57 | from sdcflows.utils.epimanip import get_trt 58 | from nipype.utils.filemanip import fname_presuffix 59 | 60 | # Generate output file name 61 | newpath = Path(newpath or ".") 62 | out_acqparams = fname_presuffix( 63 | in_file, 64 | suffix="_acqparams.txt", 65 | use_ext=False, 66 | newpath=str(newpath.absolute()), 67 | ) 68 | 69 | pe_dir = in_meta["PhaseEncodingDirection"] 70 | fsl_pe = ["0"] * 3 71 | fsl_pe["ijk".index(pe_dir[0])] = "-1" if pe_dir.endswith("-") else "1" 72 | 73 | # Write to the acqp file 74 | try: 75 | Path(out_acqparams).write_text( 76 | f"{' '.join(fsl_pe)} {get_trt(in_meta, in_file=in_file):0.7f}" 77 | ) 78 | except ValueError: 79 | Path(out_acqparams).write_text(f"{' '.join(fsl_pe)} {0.05}") 80 | 81 | out_index = fname_presuffix( 82 | in_file, 83 | suffix="_index.txt", 84 | use_ext=False, 85 | newpath=str(newpath.absolute()), 86 | ) 87 | Path(out_index).write_text(f"{' '.join(['1'] * nb.load(in_file).shape[3])}") 88 | return out_acqparams, out_index 89 | 90 | 91 | def init_eddy_wf(debug=False, name="eddy_wf"): 92 | """ 93 | Create a workflow for head-motion & Eddy currents distortion estimation with FSL. 94 | 95 | Parameters 96 | ---------- 97 | name : :obj:`str` 98 | Name of workflow (default: ``eddy_wf``) 99 | 100 | Inputs 101 | ------ 102 | dwi_file 103 | dwi NIfTI file 104 | 105 | Outputs 106 | ------- 107 | out_eddy 108 | The eddy corrected diffusion image 109 | 110 | """ 111 | from nipype.interfaces.fsl import Eddy, ExtractROI 112 | 113 | inputnode = pe.Node( 114 | niu.IdentityInterface( 115 | fields=["dwi_file", "metadata", "dwi_mask", "in_bvec", "in_bval"] 116 | ), 117 | name="inputnode", 118 | ) 119 | 120 | outputnode = pe.Node( 121 | niu.IdentityInterface( 122 | fields=["out_rotated_bvecs", "eddy_ref_image", "out_eddy"] 123 | ), 124 | name="outputnode", 125 | ) 126 | 127 | workflow = Workflow(name=name) 128 | workflow.__desc__ = f"""\ 129 | Geometrical distortions derived from the so-called Eddy-currents, and head-motion 130 | realignment parameters were estimated with the joint modeling of ``eddy_openmp``, 131 | included in FSL {Eddy().version} [@eddy]. 132 | """ 133 | eddy = pe.Node( 134 | Eddy(), 135 | name="eddy", 136 | ) 137 | 138 | if debug: 139 | eddy.inputs.niter = 1 140 | eddy.inputs.is_shelled = True 141 | eddy.inputs.dont_peas = True 142 | eddy.inputs.nvoxhp = 100 143 | 144 | # Generate the acqp and index files for eddy 145 | gen_eddy_files = pe.Node( 146 | niu.Function( 147 | input_names=["in_file", "in_meta"], 148 | output_names=["out_acqparams", "out_index"], 149 | function=gen_eddy_textfiles, 150 | ), 151 | name="gen_eddy_files", 152 | ) 153 | 154 | eddy_ref_img = pe.Node(ExtractROI(t_min=0, t_size=1), name="eddy_roi") 155 | 156 | # fmt:off 157 | workflow.connect([ 158 | (inputnode, eddy, [ 159 | ("dwi_file", "in_file"), 160 | ("dwi_mask", "in_mask"), 161 | ("in_bvec", "in_bvec"), 162 | ("in_bval", "in_bval"), 163 | ]), 164 | (inputnode, gen_eddy_files, [ 165 | ("dwi_file", "in_file"), 166 | ("metadata", "in_meta") 167 | ]), 168 | (gen_eddy_files, eddy, [ 169 | ("out_acqparams", "in_acqp"), 170 | ("out_index", "in_index"), 171 | ]), 172 | (eddy, outputnode, [ 173 | ("out_corrected", "out_eddy"), 174 | ("out_rotated_bvecs", "out_rotated_bvecs") 175 | ]), 176 | (eddy, eddy_ref_img, [("out_corrected", "in_file")]), 177 | (eddy_ref_img, outputnode, [("roi_file", "eddy_ref_image")]), 178 | ]) 179 | # fmt:on 180 | return workflow 181 | -------------------------------------------------------------------------------- /dmriprep/workflows/dwi/outputs.py: -------------------------------------------------------------------------------- 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 2 | # vi: set ft=python sts=4 ts=4 sw=4 et: 3 | # 4 | # Copyright 2021 The NiPreps Developers 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # We support and encourage derived works from this project, please read 19 | # about our expectations at 20 | # 21 | # https://www.nipreps.org/community/licensing/ 22 | # 23 | """Write outputs (derivatives and reportlets).""" 24 | from nipype.pipeline import engine as pe 25 | from nipype.interfaces import utility as niu 26 | from niworkflows.engine.workflows import LiterateWorkflow as Workflow 27 | from ...interfaces import DerivativesDataSink 28 | 29 | 30 | def init_reportlets_wf(output_dir, sdc_report=False, name="reportlets_wf"): 31 | """Set up a battery of datasinks to store reports in the right location.""" 32 | from niworkflows.interfaces.reportlets.masks import SimpleShowMaskRPT 33 | 34 | workflow = Workflow(name=name) 35 | 36 | inputnode = pe.Node( 37 | niu.IdentityInterface( 38 | fields=[ 39 | "source_file", 40 | "dwi_ref", 41 | "dwi_mask", 42 | "validation_report", 43 | "sdc_report", 44 | ] 45 | ), 46 | name="inputnode", 47 | ) 48 | mask_reportlet = pe.Node(SimpleShowMaskRPT(), name="mask_reportlet") 49 | 50 | ds_report_mask = pe.Node( 51 | DerivativesDataSink( 52 | base_directory=output_dir, desc="brain", suffix="mask", datatype="figures" 53 | ), 54 | name="ds_report_mask", 55 | run_without_submitting=True, 56 | ) 57 | ds_report_validation = pe.Node( 58 | DerivativesDataSink( 59 | base_directory=output_dir, desc="validation", datatype="figures" 60 | ), 61 | name="ds_report_validation", 62 | run_without_submitting=True, 63 | ) 64 | 65 | # fmt:off 66 | workflow.connect([ 67 | (inputnode, mask_reportlet, [("dwi_ref", "background_file"), 68 | ("dwi_mask", "mask_file")]), 69 | (inputnode, ds_report_validation, [("source_file", "source_file")]), 70 | (inputnode, ds_report_mask, [("source_file", "source_file")]), 71 | (inputnode, ds_report_validation, [("validation_report", "in_file")]), 72 | (mask_reportlet, ds_report_mask, [("out_report", "in_file")]), 73 | ]) 74 | # fmt:on 75 | if sdc_report: 76 | ds_report_sdc = pe.Node( 77 | DerivativesDataSink( 78 | base_directory=output_dir, desc="sdc", suffix="dwi", datatype="figures" 79 | ), 80 | name="ds_report_sdc", 81 | run_without_submitting=True, 82 | ) 83 | # fmt:off 84 | workflow.connect([ 85 | (inputnode, ds_report_sdc, [("source_file", "source_file"), 86 | ("sdc_report", "in_file")]), 87 | ]) 88 | # fmt:on 89 | return workflow 90 | 91 | 92 | def init_dwi_derivatives_wf(output_dir, name="dwi_derivatives_wf"): 93 | """ 94 | Set up a battery of datasinks to store dwi derivatives in the right location. 95 | 96 | Parameters 97 | ---------- 98 | output_dir : :obj:`str` 99 | Directory in which to save derivatives. 100 | name : :obj:`str` 101 | Workflow name (default: ``"dwi_derivatives_wf"``). 102 | 103 | Inputs 104 | ------ 105 | source_file 106 | One dwi file that will serve as a file naming reference. 107 | dwi_ref 108 | The b0 reference. 109 | dwi_mask 110 | The brain mask for the dwi file. 111 | 112 | """ 113 | workflow = pe.Workflow(name=name) 114 | inputnode = pe.Node( 115 | niu.IdentityInterface(fields=["source_file", "dwi_ref", "dwi_mask"]), 116 | name="inputnode", 117 | ) 118 | 119 | ds_reference = pe.Node( 120 | DerivativesDataSink( 121 | base_directory=output_dir, 122 | compress=True, 123 | suffix="epiref", 124 | datatype="dwi", 125 | ), 126 | name="ds_reference", 127 | ) 128 | 129 | ds_mask = pe.Node( 130 | DerivativesDataSink( 131 | base_directory=output_dir, 132 | compress=True, 133 | desc="brain", 134 | suffix="mask", 135 | datatype="dwi", 136 | ), 137 | name="ds_mask", 138 | ) 139 | 140 | # fmt:off 141 | workflow.connect([ 142 | (inputnode, ds_reference, [("source_file", "source_file"), 143 | ("dwi_ref", "in_file")]), 144 | (inputnode, ds_mask, [("source_file", "source_file"), 145 | ("dwi_mask", "in_file")]), 146 | ]) 147 | # fmt:on 148 | 149 | return workflow 150 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | CURBRANCH = master 10 | PYTHONPATH = $(PWD) 11 | 12 | # User-friendly check for sphinx-build 13 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 14 | $(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/) 15 | endif 16 | 17 | # Internal variables. 18 | PAPEROPT_a4 = -D latex_paper_size=a4 19 | PAPEROPT_letter = -D latex_paper_size=letter 20 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | # the i18n builder cannot share the environment and doctrees with the others 22 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 23 | 24 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 25 | 26 | help: 27 | @echo "Please use \`make ' where is one of" 28 | @echo " html to make standalone HTML files" 29 | @echo " dirhtml to make HTML files named index.html in directories" 30 | @echo " singlehtml to make a single large HTML file" 31 | @echo " pickle to make pickle files" 32 | @echo " json to make JSON files" 33 | @echo " htmlhelp to make HTML files and a HTML help project" 34 | @echo " qthelp to make HTML files and a qthelp project" 35 | @echo " devhelp to make HTML files and a Devhelp project" 36 | @echo " epub to make an epub" 37 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 38 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 39 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 40 | @echo " text to make text files" 41 | @echo " man to make manual pages" 42 | @echo " texinfo to make Texinfo files" 43 | @echo " info to make Texinfo files and run them through makeinfo" 44 | @echo " gettext to make PO message catalogs" 45 | @echo " changes to make an overview of all changed/added/deprecated items" 46 | @echo " xml to make Docutils-native XML files" 47 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 48 | @echo " linkcheck to check all external links for integrity" 49 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 50 | 51 | 52 | clean: 53 | rm -rf $(BUILDDIR)/* 54 | rm -rf reference/* 55 | rm -rf api/ 56 | 57 | html: 58 | PYTHONPATH=$(PYTHONPATH) $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 61 | 62 | dirhtml: 63 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 64 | @echo 65 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 66 | 67 | singlehtml: 68 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 69 | @echo 70 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 71 | 72 | pickle: 73 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 74 | @echo 75 | @echo "Build finished; now you can process the pickle files." 76 | 77 | json: 78 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 79 | @echo 80 | @echo "Build finished; now you can process the JSON files." 81 | 82 | htmlhelp: 83 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 84 | @echo 85 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 86 | ".hhp project file in $(BUILDDIR)/htmlhelp." 87 | 88 | qthelp: 89 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 90 | @echo 91 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 92 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 93 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/dmriprep.qhcp" 94 | @echo "To view the help file:" 95 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/dmriprep.qhc" 96 | 97 | devhelp: 98 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 99 | @echo 100 | @echo "Build finished." 101 | @echo "To view the help file:" 102 | @echo "# mkdir -p $$HOME/.local/share/devhelp/dmriprep" 103 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/dmriprep" 104 | @echo "# devhelp" 105 | 106 | epub: 107 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 108 | @echo 109 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 110 | 111 | latex: 112 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 113 | @echo 114 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 115 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 116 | "(use \`make latexpdf' here to do that automatically)." 117 | 118 | latexpdf: 119 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 120 | @echo "Running LaTeX files through pdflatex..." 121 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 122 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 123 | 124 | latexpdfja: 125 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 126 | @echo "Running LaTeX files through platex and dvipdfmx..." 127 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 128 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 129 | 130 | text: 131 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 132 | @echo 133 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 134 | 135 | man: 136 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 137 | @echo 138 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 139 | 140 | texinfo: 141 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 142 | @echo 143 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 144 | @echo "Run \`make' in that directory to run these through makeinfo" \ 145 | "(use \`make info' here to do that automatically)." 146 | 147 | info: 148 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 149 | @echo "Running Texinfo files through makeinfo..." 150 | make -C $(BUILDDIR)/texinfo info 151 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 152 | 153 | gettext: 154 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 155 | @echo 156 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 157 | 158 | changes: 159 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 160 | @echo 161 | @echo "The overview file is in $(BUILDDIR)/changes." 162 | 163 | linkcheck: 164 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 165 | @echo 166 | @echo "Link check complete; look for any errors in the above output " \ 167 | "or in $(BUILDDIR)/linkcheck/output.txt." 168 | 169 | doctest: 170 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 171 | @echo "Testing of doctests in the sources finished, look at the " \ 172 | "results in $(BUILDDIR)/doctest/output.txt." 173 | 174 | xml: 175 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 176 | @echo 177 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 178 | 179 | pseudoxml: 180 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 181 | @echo 182 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 183 | 184 | versioned: 185 | PYTHONPATH=$(PYTHONPATH) sphinx-versioning -vv -l ./docs/conf.py build -r $(CURBRANCH) ./docs/ docs/$(BUILDDIR)/html/ 186 | -------------------------------------------------------------------------------- /docs/_static/RTD-advanced-conf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/docs/_static/RTD-advanced-conf.png -------------------------------------------------------------------------------- /docs/_static/git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/docs/_static/git.png -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | Library API (application program interface) 2 | =========================================== 3 | 4 | Information on specific functions, classes, and methods. 5 | 6 | .. toctree:: 7 | :glob: 8 | 9 | api/dmriprep.config 10 | api/dmriprep.interfaces 11 | api/dmriprep.utils 12 | api/dmriprep.workflows -------------------------------------------------------------------------------- /docs/changes.rst: -------------------------------------------------------------------------------- 1 | ----------- 2 | What's new? 3 | ----------- 4 | 5 | .. include:: ../CHANGES.rst 6 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: links.rst 2 | .. include:: ../README.rst 3 | 4 | Contents 5 | -------- 6 | .. toctree:: 7 | :maxdepth: 3 8 | 9 | installation 10 | usage 11 | api 12 | changes 13 | roadmap 14 | 15 | 16 | The overall philosophy of the NiPreps and some examples are explained in this video: 17 | 18 | .. raw:: html 19 | 20 |
21 | 22 |
23 | 24 | 25 | About the *NiPreps* framework licensing 26 | --------------------------------------- 27 | Please check https://www.nipreps.org/community/licensing/ for detailed 28 | information on the criteria we use to license *fMRIPrep* and other 29 | projects of the framework. 30 | 31 | License information 32 | ------------------- 33 | Copyright (c) 2021, the *NiPreps* Developers. 34 | 35 | *dMRIPrep* is licensed under the Apache License, Version 2.0 (the "License"); 36 | you may not use this file except in compliance with the License. 37 | You may obtain a copy of the License at 38 | http://www.apache.org/licenses/LICENSE-2.0 39 | 40 | Unless required by applicable law or agreed to in writing, software 41 | distributed under the License is distributed on an "AS IS" BASIS, 42 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 43 | See the License for the specific language governing permissions and 44 | limitations under the License. 45 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. include:: links.rst 2 | 3 | ------------ 4 | Installation 5 | ------------ 6 | There are two ways of getting *dMRIPrep* installed: 7 | 8 | * within a `Manually Prepared Environment (Python 3.5+)`_, also known as 9 | *bare-metal installation*; or 10 | * using container technologies (RECOMMENDED), such as 11 | `Docker `__ or 12 | `Singularity `__. 13 | 14 | Once you have your *bare-metal* environment set-up (first option above), 15 | the next step is executing the ``dmriprep`` command-line. 16 | The ``dmriprep`` command-line options are documented in the :ref:`usage` 17 | section. 18 | The ``dmriprep`` command-line adheres to the `BIDS-Apps recommendations 19 | for the user interface `__. 20 | Therefore, the command-line has the following structure: 21 | :: 22 | 23 | $ dmriprep 24 | 25 | On the other hand, if you chose a container infrastructure, then 26 | the command-line will be composed of a preamble to configure the 27 | container execution followed by the ``dmriprep`` command-line options 28 | as if you were running it on a *bare-metal* installation. 29 | The command-line structure above is then modified as follows: 30 | :: 31 | 32 | $ \ 33 | 34 | 35 | Therefore, once specified, the container options and the image to be run 36 | from the command line is the same as for the *bare-metal* installation but dropping 37 | the ``dmriprep`` executable name. 38 | 39 | Container technologies: Docker and Singularity 40 | ============================================== 41 | Container technologies are operating-system-level virtualization methods to run Linux systems 42 | using the host's Linux kernel. 43 | This is a lightweight approach to virtualization, as compares to virtual machines. 44 | 45 | 46 | .. _installation_docker: 47 | 48 | Docker (recommended for PC/laptop and commercial Cloud) 49 | ------------------------------------------------------- 50 | Probably, the most popular framework to execute containers is Docker. 51 | If you are to run *dMRIPrep* on your PC/laptop, this is the RECOMMENDED way of execution. 52 | Please make sure you follow the `Docker installation`_ instructions. 53 | You can check your `Docker Engine`_ installation running their ``hello-world`` image: :: 54 | 55 | $ docker run --rm hello-world 56 | 57 | If you have a functional installation, then you should obtain the following output. :: 58 | 59 | Hello from Docker! 60 | This message shows that your installation appears to be working correctly. 61 | 62 | To generate this message, Docker took the following steps: 63 | 1. The Docker client contacted the Docker daemon. 64 | 1. The Docker daemon pulled the "hello-world" image from the Docker Hub. 65 | (amd64) 66 | 1. The Docker daemon created a new container from that image which runs the 67 | executable that produces the output you are currently reading. 68 | 1. The Docker daemon streamed that output to the Docker client, which sent it 69 | to your terminal. 70 | 71 | To try something more ambitious, you can run an Ubuntu container with: 72 | $ docker run -it ubuntu bash 73 | 74 | Share images, automate workflows, and more with a free Docker ID: 75 | https://hub.docker.com/ 76 | 77 | For more examples and ideas, visit: 78 | https://docs.docker.com/get-started/ 79 | 80 | After checking your Docker Engine is capable of running Docker images, then go ahead 81 | and `check out our documentation `__ 82 | to run the *dMRIPrep* image. 83 | The list of Docker images ready to use is found at the `Docker Hub`_, 84 | under the ``nipreps/dmriprep`` identifier. 85 | 86 | Singularity (recommended for HPC) 87 | --------------------------------- 88 | For security reasons, many :abbr:`HPCs (High-Performance Computing)` (e.g., TACC_) 89 | do not allow Docker containers, but do allow Singularity_ containers. 90 | The improved security for multi-tenant systems comes at the price of some limitations 91 | and extra steps necessary for execution. 92 | Please make sure you `follow our tips and tricks to run dMRIPrep's Singularity images 93 | `_. 94 | 95 | 96 | Manually Prepared Environment (Python 3.5+) 97 | =========================================== 98 | .. warning:: 99 | 100 | This method is not recommended! Please checkout container alternatives 101 | such as `Docker `__, or 102 | `Singularity `__. 103 | 104 | Make sure all of *dMRIPrep*'s `External Dependencies`_ are installed. 105 | These tools must be installed and their binaries available in the 106 | system's ``$PATH``. 107 | A relatively interpretable description of how your environment can be set-up 108 | is found in the `Dockerfile `_. 109 | As an additional installation setting, FreeSurfer requires a license file (see :ref:`fs_license`). 110 | 111 | On a functional Python 3.7 (or above) environment with ``pip`` installed, 112 | *dMRIPrep* can be installed using the habitual command :: 113 | 114 | $ python -m pip install dmriprep 115 | 116 | Check your installation with the ``--version`` argument :: 117 | 118 | $ dmriprep --version 119 | 120 | 121 | External Dependencies 122 | --------------------- 123 | *dMRIPrep* is written using Python 3.7 (or above), and is based on 124 | Nipype_. 125 | 126 | *dMRIPrep* requires some other neuroimaging software tools that are 127 | not handled by the Python's packaging system (Pypi) used to deploy 128 | the ``dmriprep`` package: 129 | 130 | - FSL_ (version 5.0.9) 131 | - ANTs_ (version 2.3.3 - NeuroDocker build) 132 | - AFNI_ (version Debian-16.2.07) 133 | - FreeSurfer_ (version 6.0.1) 134 | - `bids-validator `_ (version 1.4.0) 135 | -------------------------------------------------------------------------------- /docs/links.rst: -------------------------------------------------------------------------------- 1 | .. _fMRIPrep: https://github.com/poldracklab/fmriprep 2 | .. _FreeSurfer: https://surfer.nmr.mgh.harvard.edu/ 3 | .. _Nipype: http://nipype.readthedocs.io/en/latest/ 4 | .. _BIDS: http://bids.neuroimaging.io 5 | .. _Installation: installation.html 6 | .. _workflows: workflows.html 7 | .. _FSL: https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/ 8 | .. _ANTs: https://stnava.github.io/ANTs/ 9 | .. _AFNI: https://afni.nimh.nih.gov/ 10 | .. _`Docker Engine`: https://www.docker.com/products/container-runtime 11 | .. _`Docker installation`: https://docs.docker.com/install/ 12 | .. _`Docker Hub`: https://hub.docker.com/r/nipreps/dmriprep/tags 13 | .. _Singularity: https://github.com/singularityware/singularity 14 | .. _TACC: https://www.tacc.utexas.edu/ 15 | -------------------------------------------------------------------------------- /docs/reference/.gitignore: -------------------------------------------------------------------------------- 1 | *.rst 2 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | git+https://github.com/AleksandarPetrov/napoleon.git@0dc3f28a309ad602be5f44a9049785a1026451b3#egg=sphinxcontrib-napoleon 2 | git+https://github.com/rwblair/sphinxcontrib-versioning.git@39b40b0b84bf872fc398feff05344051bbce0f63#egg=sphinxcontrib-versioning 3 | nbsphinx 4 | nipype ~= 1.5.1 5 | niworkflows >=1.4.0rc5,<1.5 6 | packaging 7 | pydot>=1.2.3 8 | pydotplus 9 | sdcflows ~= 2.0.4 10 | smriprep >= 0.8.0rc2 11 | sphinx >=2.1.2,<3.0 12 | sphinx-argparse 13 | sphinx_rtd_theme 14 | sphinxcontrib-apidoc ~= 0.3.0 15 | templateflow 16 | toml 17 | -------------------------------------------------------------------------------- /docs/resources/figure1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipreps/dmriprep/8465bb2f768b661c0f9adb4b0ba6eaddad52f000/docs/resources/figure1.png -------------------------------------------------------------------------------- /docs/roadmap.rst: -------------------------------------------------------------------------------- 1 | .. include:: links.rst 2 | 3 | Development road-map 4 | ==================== 5 | This road-map serves as a guide for developers as well as a way for us to 6 | communicate to users and other stake-holders about the expectations they should 7 | have for the current functionality of the software and future developments. 8 | 9 | If you would like to be part of the team developing this road-map, please be sure to 10 | read our `Contributors Guidelines `__. 11 | Then, you can contact the developers through the `Mattermost channel `__ to be invited to our bi-weekly meetings. 12 | 13 | This road-map proposes a :abbr:`RERO (release early, release often)` philosophy, scheduling 14 | a monthly release until the first stable 1.0 release is reached. 15 | 16 | .. important:: 17 | 18 | Updated: Dec 18, 2020 19 | Latest release: `0.3.0 (October 13, 2020) `__. 20 | 21 | Version 0.4 (Before end of 2020) 22 | -------------------------------- 23 | Version 0.4 will condense all the outcomes of our sprint towards ISMRM's 2021 abstracts deadline. 24 | This mostly includes house-keeping work, and most prominently, the integration of the *SDCFlows* 2.0 25 | alpha releases, which makes *dMRIPrep* go ahead of *fMRIPrep* in addressing distortions caused by 26 | :math:`B_0` inhomogeneity. 27 | 28 | This release will also include Salim's efforts in `#144 `__ 29 | to provide a temporary implementation of head-motion and eddy-currents correction using 30 | FSL's ``eddy``. 31 | This temporary solution will be replaced by our 3dSHORE-based algorithm ported from QSIPREP, 32 | and left in place for researchers who prefer this option. 33 | 34 | Version 0.5 (January, 2021) 35 | --------------------------- 36 | #. Continue with the *SDCFlows 2.0* integration: 37 | 38 | - Cover more complex fieldmap specifications 39 | - Automatically set up "*fieldmap-less*" estimations 40 | 41 | #. Framewise-displacement (or equivalent) calculation 42 | 43 | We will identify volumes that are outliers in terms of head-motion, or other 44 | severe artifacts that make them likely candidates for exclusion from further 45 | analysis. 46 | Regarding the *or equivalent* note above: following with `this conversation 47 | `__, 48 | it could be interesting to calculate some sort of average displacement of voxels 49 | within the white-matter mask instead. 50 | 51 | #. Finalize ongoing PRs about reporting number of shells 52 | 53 | - `#73 `__ 54 | - `#129 `__ 55 | 56 | #. First draft of ISBI 2021 tutorial: 57 | 58 | - Accept the design for our ISBI 2021 tutorial and document it on the notebooks repo. 59 | - First draft 60 | - Start development 61 | - Plan for supporting Derek and Ariel in taking the head-motion correction to the finish line. 62 | 63 | Version 0.6 (February, 2021) 64 | ---------------------------- 65 | #. Head motion correction. 66 | 67 | A SHOREline-based approach, ported from QSIPREP. In cases where the data are 68 | "shelled", 3dSHORE will be used as the diffusion model. If the data are 69 | single-shell, we will use SFM as the diffusion model. 70 | 71 | #. :math:`B_1` inhomogeneity correction 72 | 73 | - Decide whether it can be brought around from estimation on T1w images 74 | - Decide whether it should be a default-off option that can be enabled with 75 | a flag, or else, generate both conversions always. 76 | 77 | #. Initiate Phase I of testing 78 | 79 | - Compose our test-bed dataset 80 | - Document Phase I testing and reporting protocols 81 | - Start execution 82 | 83 | # Continue with the development of ISBI 2021 tutorial 84 | 85 | Version 0.7 (March, 2021) 86 | ------------------------- 87 | The *noisy month*. This is not a musical event, but a development cycle where we will 88 | focus on the implementation of steps addressing noise in DWI: 89 | 90 | #. Identification of outlier measurements (+ imputation?) 91 | 92 | #. Implementation of component-based noise identification techniques 93 | 94 | - Comparison of multiple approaches including MP-PCA, NLMeans, and Patch2Self (`#132 `__) 95 | 96 | #. Gibbs-ringing: investigate whether it should be estimated if other techniques 97 | are in place (i.e., component-based above), and ordering of steps. 98 | 99 | #. Rician bias modeling. 100 | 101 | #. DWI carpet-plot and confounds collation. 102 | 103 | #. Testing Phase I execution 104 | 105 | #. Final release of the ISBI 2021 tutorial 106 | 107 | Version 0.8 (April, 2021) 108 | ------------------------- 109 | This release will only address bugfixes conducive to finishing evaluation Phase I, 110 | which should conform a pretty solid ensemble ready for premiere in ISMRM 2021. 111 | 112 | Version 0.9 (May, 2021) 113 | ----------------------- 114 | #. First official presentation at ISMRM 2021 (should the abstract be accepted) 115 | #. Evaluation Phase II starts. 116 | 117 | - Determine an appropriate dataset 118 | - Plan for benchmarking experiments (`#121 `__) 119 | - Start with addressing issues as they are reported 120 | 121 | Version 1.0 (Targetted for September 2021) 122 | ------------------------------------------ 123 | Wrap-up evaluation Phase II with the first stable release of *dMRIPrep*. 124 | 125 | Long-term plans 126 | --------------- 127 | In the long run we would like to explore the following processing steps: 128 | 129 | - Gradient non-linearity correction 130 | -------------------------------------------------------------------------------- /docs/sphinxext/github.py: -------------------------------------------------------------------------------- 1 | """Define text roles for GitHub 2 | 3 | * ghissue - Issue 4 | * ghpull - Pull Request 5 | * ghuser - User 6 | 7 | Adapted from bitbucket example here: 8 | https://bitbucket.org/birkenfeld/sphinx-contrib/src/tip/bitbucket/sphinxcontrib/bitbucket.py 9 | 10 | Authors 11 | ------- 12 | 13 | * Doug Hellmann 14 | * Min RK 15 | """ 16 | # 17 | # Original Copyright (c) 2010 Doug Hellmann. All rights reserved. 18 | # 19 | 20 | from docutils import nodes, utils 21 | from docutils.parsers.rst.roles import set_classes 22 | 23 | 24 | def make_link_node(rawtext, app, type, slug, options): 25 | """Create a link to a github resource. 26 | 27 | :param rawtext: Text being replaced with link node. 28 | :param app: Sphinx application context 29 | :param type: Link type (issues, changeset, etc.) 30 | :param slug: ID of the thing to link to 31 | :param options: Options dictionary passed to role func. 32 | """ 33 | 34 | try: 35 | base = app.config.github_project_url 36 | if not base: 37 | raise AttributeError 38 | if not base.endswith("/"): 39 | base += "/" 40 | except AttributeError as err: 41 | raise ValueError( 42 | "github_project_url configuration value is not set (%s)" % str(err) 43 | ) 44 | 45 | ref = base + type + "/" + slug + "/" 46 | set_classes(options) 47 | prefix = "#" 48 | if type == "pull": 49 | prefix = "PR " + prefix 50 | node = nodes.reference( 51 | rawtext, prefix + utils.unescape(slug), refuri=ref, **options 52 | ) 53 | return node 54 | 55 | 56 | def ghissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): 57 | """Link to a GitHub issue. 58 | 59 | Returns 2 part tuple containing list of nodes to insert into the 60 | document and a list of system messages. Both are allowed to be 61 | empty. 62 | 63 | :param name: The role name used in the document. 64 | :param rawtext: The entire markup snippet, with role. 65 | :param text: The text marked with the role. 66 | :param lineno: The line number where rawtext appears in the input. 67 | :param inliner: The inliner instance that called us. 68 | :param options: Directive options for customization. 69 | :param content: The directive content for customization. 70 | """ 71 | 72 | try: 73 | issue_num = int(text) 74 | if issue_num <= 0: 75 | raise ValueError 76 | except ValueError: 77 | msg = inliner.reporter.error( 78 | "GitHub issue number must be a number greater than or equal to 1; " 79 | '"%s" is invalid.' % text, 80 | line=lineno, 81 | ) 82 | prb = inliner.problematic(rawtext, rawtext, msg) 83 | return [prb], [msg] 84 | app = inliner.document.settings.env.app 85 | # app.info('issue %r' % text) 86 | if "pull" in name.lower(): 87 | category = "pull" 88 | elif "issue" in name.lower(): 89 | category = "issues" 90 | else: 91 | msg = inliner.reporter.error( 92 | 'GitHub roles include "ghpull" and "ghissue", ' '"%s" is invalid.' % name, 93 | line=lineno, 94 | ) 95 | prb = inliner.problematic(rawtext, rawtext, msg) 96 | return [prb], [msg] 97 | node = make_link_node(rawtext, app, category, str(issue_num), options) 98 | return [node], [] 99 | 100 | 101 | def ghuser_role(name, rawtext, text, lineno, inliner, options={}, content=[]): 102 | """Link to a GitHub user. 103 | 104 | Returns 2 part tuple containing list of nodes to insert into the 105 | document and a list of system messages. Both are allowed to be 106 | empty. 107 | 108 | :param name: The role name used in the document. 109 | :param rawtext: The entire markup snippet, with role. 110 | :param text: The text marked with the role. 111 | :param lineno: The line number where rawtext appears in the input. 112 | :param inliner: The inliner instance that called us. 113 | :param options: Directive options for customization. 114 | :param content: The directive content for customization. 115 | """ 116 | app = inliner.document.settings.env.app 117 | # app.info('user link %r' % text) 118 | ref = "https://www.github.com/" + text 119 | node = nodes.reference(rawtext, text, refuri=ref, **options) 120 | return [node], [] 121 | 122 | 123 | def ghcommit_role(name, rawtext, text, lineno, inliner, options={}, content=[]): 124 | """Link to a GitHub commit. 125 | 126 | Returns 2 part tuple containing list of nodes to insert into the 127 | document and a list of system messages. Both are allowed to be 128 | empty. 129 | 130 | :param name: The role name used in the document. 131 | :param rawtext: The entire markup snippet, with role. 132 | :param text: The text marked with the role. 133 | :param lineno: The line number where rawtext appears in the input. 134 | :param inliner: The inliner instance that called us. 135 | :param options: Directive options for customization. 136 | :param content: The directive content for customization. 137 | """ 138 | app = inliner.document.settings.env.app 139 | # app.info('user link %r' % text) 140 | try: 141 | base = app.config.github_project_url 142 | if not base: 143 | raise AttributeError 144 | if not base.endswith("/"): 145 | base += "/" 146 | except AttributeError as err: 147 | raise ValueError( 148 | "github_project_url configuration value is not set (%s)" % str(err) 149 | ) 150 | 151 | ref = base + text 152 | node = nodes.reference(rawtext, text[:6], refuri=ref, **options) 153 | return [node], [] 154 | 155 | 156 | def setup(app): 157 | """Install the plugin. 158 | 159 | :param app: Sphinx application context. 160 | """ 161 | app.info("Initializing GitHub plugin") 162 | app.add_role("ghissue", ghissue_role) 163 | app.add_role("ghpull", ghissue_role) 164 | app.add_role("ghuser", ghuser_role) 165 | app.add_role("ghcommit", ghcommit_role) 166 | app.add_config_value("github_project_url", None, "env") 167 | return 168 | -------------------------------------------------------------------------------- /docs/sphinxext/github_link.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script comes from scikit-learn: 3 | https://github.com/scikit-learn/scikit-learn/blob/master/doc/sphinxext/github_link.py 4 | """ 5 | from operator import attrgetter 6 | import inspect 7 | import subprocess 8 | import os 9 | import sys 10 | from functools import partial 11 | 12 | REVISION_CMD = "git rev-parse --short HEAD" 13 | 14 | 15 | def _get_git_revision(): 16 | try: 17 | revision = subprocess.check_output(REVISION_CMD.split()).strip() 18 | except (subprocess.CalledProcessError, OSError): 19 | print("Failed to execute git to get revision") 20 | return None 21 | return revision.decode("utf-8") 22 | 23 | 24 | def _linkcode_resolve(domain, info, package, url_fmt, revision): 25 | """Determine a link to online source for a class/method/function 26 | 27 | This is called by sphinx.ext.linkcode 28 | 29 | An example with a long-untouched module that everyone has 30 | >>> _linkcode_resolve('py', {'module': 'tty', 31 | ... 'fullname': 'setraw'}, 32 | ... package='tty', 33 | ... url_fmt='http://hg.python.org/cpython/file/' 34 | ... '{revision}/Lib/{package}/{path}#L{lineno}', 35 | ... revision='xxxx') 36 | 'http://hg.python.org/cpython/file/xxxx/Lib/tty/tty.py#L18' 37 | """ 38 | 39 | if revision is None: 40 | return 41 | if domain not in ("py", "pyx"): 42 | return 43 | if not info.get("module") or not info.get("fullname"): 44 | return 45 | 46 | class_name = info["fullname"].split(".")[0] 47 | if type(class_name) != str: 48 | # Python 2 only 49 | class_name = class_name.encode("utf-8") 50 | module = __import__(info["module"], fromlist=[class_name]) 51 | obj = attrgetter(info["fullname"])(module) 52 | 53 | try: 54 | fn = inspect.getsourcefile(obj) 55 | except Exception: 56 | fn = None 57 | if not fn: 58 | try: 59 | fn = inspect.getsourcefile(sys.modules[obj.__module__]) 60 | except Exception: 61 | fn = None 62 | if not fn: 63 | return 64 | 65 | fn = os.path.relpath(fn, start=os.path.dirname(__import__(package).__file__)) 66 | try: 67 | lineno = inspect.getsourcelines(obj)[1] 68 | except Exception: 69 | lineno = "" 70 | return url_fmt.format(revision=revision, package=package, path=fn, lineno=lineno) 71 | 72 | 73 | def make_linkcode_resolve(package, url_fmt): 74 | """Returns a linkcode_resolve function for the given URL format 75 | 76 | revision is a git commit reference (hash or name) 77 | 78 | package is the name of the root module of the package 79 | 80 | url_fmt is along the lines of ('https://github.com/USER/PROJECT/' 81 | 'blob/{revision}/{package}/' 82 | '{path}#L{lineno}') 83 | """ 84 | revision = _get_git_revision() 85 | return partial( 86 | _linkcode_resolve, revision=revision, package=package, url_fmt=url_fmt 87 | ) 88 | -------------------------------------------------------------------------------- /docs/sphinxext/math_dollar.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | def dollars_to_math(source): 5 | r""" 6 | Replace dollar signs with backticks. 7 | 8 | More precisely, do a regular expression search. Replace a plain 9 | dollar sign ($) by a backtick (`). Replace an escaped dollar sign 10 | (\$) by a dollar sign ($). Don't change a dollar sign preceded or 11 | followed by a backtick (`$ or $`), because of strings like 12 | "``$HOME``". Don't make any changes on lines starting with 13 | spaces, because those are indented and hence part of a block of 14 | code or examples. 15 | 16 | This also doesn't replaces dollar signs enclosed in curly braces, 17 | to avoid nested math environments, such as :: 18 | 19 | $f(n) = 0 \text{ if $n$ is prime}$ 20 | 21 | Thus the above line would get changed to 22 | 23 | `f(n) = 0 \text{ if $n$ is prime}` 24 | """ 25 | s = "\n".join(source) 26 | if s.find("$") == -1: 27 | return 28 | # This searches for "$blah$" inside a pair of curly braces -- 29 | # don't change these, since they're probably coming from a nested 30 | # math environment. So for each match, we replace it with a temporary 31 | # string, and later on we substitute the original back. 32 | global _data 33 | _data = {} 34 | 35 | def repl(matchobj): 36 | global _data 37 | s = matchobj.group(0) 38 | t = "___XXX_REPL_%d___" % len(_data) 39 | _data[t] = s 40 | return t 41 | 42 | s = re.sub(r"({[^{}$]*\$[^{}$]*\$[^{}]*})", repl, s) 43 | # matches $...$ 44 | dollars = re.compile(r"(?= 3: 35 | sixu = lambda s: s 36 | else: 37 | sixu = lambda s: unicode(s, "unicode_escape") 38 | 39 | 40 | def mangle_docstrings(app, what, name, obj, options, lines, reference_offset=[0]): 41 | 42 | cfg = { 43 | "use_plots": app.config.numpydoc_use_plots, 44 | "show_class_members": app.config.numpydoc_show_class_members, 45 | "show_inherited_class_members": app.config.numpydoc_show_inherited_class_members, 46 | "class_members_toctree": app.config.numpydoc_class_members_toctree, 47 | } 48 | 49 | u_NL = sixu("\n") 50 | if what == "module": 51 | # Strip top title 52 | pattern = "^\\s*[#*=]{4,}\\n[a-z0-9 -]+\\n[#*=]{4,}\\s*" 53 | title_re = re.compile(sixu(pattern), re.I | re.S) 54 | lines[:] = title_re.sub(sixu(""), u_NL.join(lines)).split(u_NL) 55 | else: 56 | doc = get_doc_object(obj, what, u_NL.join(lines), config=cfg) 57 | if sys.version_info[0] >= 3: 58 | doc = str(doc) 59 | else: 60 | doc = unicode(doc) 61 | lines[:] = doc.split(u_NL) 62 | 63 | if app.config.numpydoc_edit_link and hasattr(obj, "__name__") and obj.__name__: 64 | if hasattr(obj, "__module__"): 65 | v = dict(full_name=sixu("%s.%s") % (obj.__module__, obj.__name__)) 66 | else: 67 | v = dict(full_name=obj.__name__) 68 | lines += [sixu(""), sixu(".. htmlonly::"), sixu("")] 69 | lines += [ 70 | sixu(" %s") % x for x in (app.config.numpydoc_edit_link % v).split("\n") 71 | ] 72 | 73 | # replace reference numbers so that there are no duplicates 74 | references = [] 75 | for line in lines: 76 | line = line.strip() 77 | m = re.match(sixu("^.. \\[([a-z0-9_.-])\\]"), line, re.I) 78 | if m: 79 | references.append(m.group(1)) 80 | 81 | # start renaming from the longest string, to avoid overwriting parts 82 | references.sort(key=lambda x: -len(x)) 83 | if references: 84 | for i, line in enumerate(lines): 85 | for r in references: 86 | if re.match(sixu("^\\d+$"), r): 87 | new_r = sixu("R%d") % (reference_offset[0] + int(r)) 88 | else: 89 | new_r = sixu("%s%d") % (r, reference_offset[0]) 90 | lines[i] = lines[i].replace(sixu("[%s]_") % r, sixu("[%s]_") % new_r) 91 | lines[i] = lines[i].replace( 92 | sixu(".. [%s]") % r, sixu(".. [%s]") % new_r 93 | ) 94 | 95 | reference_offset[0] += len(references) 96 | 97 | 98 | def mangle_signature(app, what, name, obj, options, sig, retann): 99 | # Do not try to inspect classes that don't define `__init__` 100 | if inspect.isclass(obj) and ( 101 | not hasattr(obj, "__init__") 102 | or "initializes x; see " in pydoc.getdoc(obj.__init__) 103 | ): 104 | return "", "" 105 | 106 | if not ( 107 | isinstance(obj, collections.Callable) or hasattr(obj, "__argspec_is_invalid_") 108 | ): 109 | return 110 | 111 | if not hasattr(obj, "__doc__"): 112 | return 113 | 114 | doc = SphinxDocString(pydoc.getdoc(obj)) 115 | if doc["Signature"]: 116 | sig = re.sub(sixu("^[^(]*"), sixu(""), doc["Signature"]) 117 | return sig, sixu("") 118 | 119 | 120 | def setup(app, get_doc_object_=get_doc_object): 121 | if not hasattr(app, "add_config_value"): 122 | return # probably called by nose, better bail out 123 | 124 | global get_doc_object 125 | get_doc_object = get_doc_object_ 126 | 127 | app.connect("autodoc-process-docstring", mangle_docstrings) 128 | app.connect("autodoc-process-signature", mangle_signature) 129 | app.add_config_value("numpydoc_edit_link", None, False) 130 | app.add_config_value("numpydoc_use_plots", None, False) 131 | app.add_config_value("numpydoc_show_class_members", True, True) 132 | app.add_config_value("numpydoc_show_inherited_class_members", True, True) 133 | app.add_config_value("numpydoc_class_members_toctree", True, True) 134 | 135 | # Extra mangling domains 136 | app.add_domain(NumpyPythonDomain) 137 | app.add_domain(NumpyCDomain) 138 | 139 | 140 | # ------------------------------------------------------------------------------ 141 | # Docstring-mangling domains 142 | # ------------------------------------------------------------------------------ 143 | 144 | from docutils.statemachine import ViewList 145 | from sphinx.domains.c import CDomain 146 | from sphinx.domains.python import PythonDomain 147 | 148 | 149 | class ManglingDomainBase(object): 150 | directive_mangling_map = {} 151 | 152 | def __init__(self, *a, **kw): 153 | super(ManglingDomainBase, self).__init__(*a, **kw) 154 | self.wrap_mangling_directives() 155 | 156 | def wrap_mangling_directives(self): 157 | for name, objtype in list(self.directive_mangling_map.items()): 158 | self.directives[name] = wrap_mangling_directive( 159 | self.directives[name], objtype 160 | ) 161 | 162 | 163 | class NumpyPythonDomain(ManglingDomainBase, PythonDomain): 164 | name = "np" 165 | directive_mangling_map = { 166 | "function": "function", 167 | "class": "class", 168 | "exception": "class", 169 | "method": "function", 170 | "classmethod": "function", 171 | "staticmethod": "function", 172 | "attribute": "attribute", 173 | } 174 | indices = [] 175 | 176 | 177 | class NumpyCDomain(ManglingDomainBase, CDomain): 178 | name = "np-c" 179 | directive_mangling_map = { 180 | "function": "function", 181 | "member": "attribute", 182 | "macro": "function", 183 | "type": "class", 184 | "var": "object", 185 | } 186 | 187 | 188 | def wrap_mangling_directive(base_directive, objtype): 189 | class directive(base_directive): 190 | def run(self): 191 | env = self.state.document.settings.env 192 | 193 | name = None 194 | if self.arguments: 195 | m = re.match(r"^(.*\s+)?(.*?)(\(.*)?", self.arguments[0]) 196 | name = m.group(2).strip() 197 | 198 | if not name: 199 | name = self.arguments[0] 200 | 201 | lines = list(self.content) 202 | mangle_docstrings(env.app, objtype, name, None, None, lines) 203 | self.content = ViewList(lines, self.content.parent) 204 | 205 | return base_directive.run(self) 206 | 207 | return directive 208 | -------------------------------------------------------------------------------- /docs/tools/LICENSE.txt: -------------------------------------------------------------------------------- 1 | These files were obtained from 2 | 3 | https://www.mail-archive.com/sphinx-dev@googlegroups.com/msg02472.html 4 | 5 | and were released under a BSD/MIT license by Fernando Perez, Matthew Brett and 6 | the PyMVPA folks. Further cleanups by the scikit-image crew. 7 | 8 | -------------------------------------------------------------------------------- /docs/tools/buildmodref.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Script to auto-generate API docs. 3 | """ 4 | from __future__ import print_function, division 5 | 6 | # stdlib imports 7 | import sys 8 | import re 9 | 10 | # local imports 11 | from apigen import ApiDocWriter 12 | 13 | # version comparison 14 | from distutils.version import LooseVersion as V 15 | 16 | # ***************************************************************************** 17 | 18 | 19 | def abort(error): 20 | print("*WARNING* API documentation not generated: %s" % error) 21 | exit() 22 | 23 | 24 | def writeapi(package, outdir, source_version, other_defines=True): 25 | # Check that the package is available. If not, the API documentation is not 26 | # (re)generated and existing API documentation sources will be used. 27 | 28 | try: 29 | __import__(package) 30 | except ImportError: 31 | abort("Can not import " + package) 32 | 33 | module = sys.modules[package] 34 | 35 | # Check that the source version is equal to the installed 36 | # version. If the versions mismatch the API documentation sources 37 | # are not (re)generated. This avoids automatic generation of documentation 38 | # for older or newer versions if such versions are installed on the system. 39 | 40 | installed_version = V(module.__version__) 41 | if source_version != installed_version: 42 | abort("Installed version does not match source version") 43 | 44 | docwriter = ApiDocWriter(package, rst_extension=".rst", other_defines=other_defines) 45 | 46 | docwriter.package_skip_patterns += [ 47 | r"\.%s$" % package, 48 | r".*test.*$", 49 | r"\.version.*$", 50 | ] 51 | docwriter.write_api_docs(outdir) 52 | docwriter.write_index(outdir, "index", relative_to=outdir) 53 | print("%d files written" % len(docwriter.written_modules)) 54 | 55 | 56 | if __name__ == "__main__": 57 | package = sys.argv[1] 58 | outdir = sys.argv[2] 59 | try: 60 | other_defines = sys.argv[3] 61 | except IndexError: 62 | other_defines = True 63 | else: 64 | other_defines = other_defines in ("True", "true", "1") 65 | 66 | writeapi(package, outdir, other_defines=other_defines) 67 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | .. include:: links.rst 2 | 3 | .. _Usage : 4 | 5 | ----- 6 | Usage 7 | ----- 8 | 9 | Execution and the BIDS format 10 | ----------------------------- 11 | The *dMRIPrep* workflow takes as principal input the path of the dataset 12 | that is to be processed. 13 | The input dataset is required to be in valid :abbr:`BIDS (Brain Imaging Data 14 | Structure)` format, and it must include at least one T1w structural image and 15 | (unless disabled with a flag) one :abbr:`DWI (diffusion weighted imaging)` series. 16 | We highly recommend that you validate your dataset with the free, online 17 | `BIDS Validator `_. 18 | 19 | The exact command to run *dMRIPrep* depends on the Installation_ method. 20 | The common parts of the command follow the `BIDS-Apps 21 | `_ definition. 22 | Example: :: 23 | 24 | dmriprep data/bids_root/ out/ participant -w work/ 25 | 26 | 27 | Command-Line Arguments 28 | ---------------------- 29 | .. argparse:: 30 | :ref: dmriprep.cli.parser._build_parser 31 | :prog: dmriprep 32 | :nodefault: 33 | :nodefaultconst: 34 | 35 | .. _fs_license: 36 | 37 | The FreeSurfer license 38 | ---------------------- 39 | *dMRIPrep* uses FreeSurfer tools, which require a license to run. 40 | 41 | To obtain a FreeSurfer license, simply register for free at 42 | https://surfer.nmr.mgh.harvard.edu/registration.html. 43 | 44 | When using manually-prepared environments or Singularity, FreeSurfer will search 45 | for a license key file first using the ``$FS_LICENSE`` environment variable and then 46 | in the default path to the license key file (``$FREESURFER_HOME/license.txt``). 47 | If using the ``--cleanenv`` flag and ``$FS_LICENSE`` is set, use ``--fs-license-file $FS_LICENSE`` 48 | to pass the license file location to *dMRIPrep*. 49 | 50 | It is possible to run the Docker container pointing the image to a local path 51 | where a valid license file is stored. 52 | For example, if the license is stored in the ``$HOME/.licenses/freesurfer/license.txt`` 53 | file on the host system: :: 54 | 55 | $ docker run -ti --rm \ 56 | -v $HOME/fullds005:/data:ro \ 57 | -v $HOME/dockerout:/out \ 58 | -v $HOME/.licenses/freesurfer/license.txt:/opt/freesurfer/license.txt \ 59 | nipreps/dmriprep:latest \ 60 | /data /out/out \ 61 | participant \ 62 | --ignore fieldmaps 63 | 64 | 65 | Usage tracking with Google Analytics 66 | ------------------------------------ 67 | To be able to assess usage of the software, we are recording each use of the 68 | :abbr:`CLI (command-line interface)` as an event in Google Analytics, 69 | using `popylar `__. 70 | `` 71 | 72 | For now, the only information that we are recording is the fact that the CLI was 73 | called and whether the call completed successfully. In addition, through Google 74 | Analytics, we will have access to very general information, such as the country 75 | and city in which the computer using the CLI was located and the time that it 76 | was used. At this time, we do not record any additional information, although in 77 | the future we may want to record statistics on the computational environment in 78 | which the CLI was called, such as the operating system. 79 | 80 | Opting out of this usage tracking can be done by calling the CLI with the 81 | ``--notrack`` flag:: 82 | 83 | dmriprep data/bids_root/ out/ participant -w work/ --notrack 84 | -------------------------------------------------------------------------------- /get_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 3 | # vi: set ft=python sts=4 ts=4 sw=4 et: 4 | """A convenience tool for querying dMRIPrep's version.""" 5 | import sys 6 | import os.path as op 7 | 8 | 9 | def main(): 10 | """Print current dMRIPrep version.""" 11 | sys.path.insert(0, op.abspath(".")) 12 | from dmriprep.__about__ import __version__ 13 | 14 | print(__version__) 15 | 16 | 17 | if __name__ == "__main__": 18 | main() 19 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | # setuptools can be configured with setup.cfg when >=30.3 3 | # Most bugfixes regarding setup.cfg are included before 38.3 4 | requires = ["setuptools >= 40.8.0", "wheel"] 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | author = The NiPreps developers 3 | author_email = nipreps@gmail.com 4 | classifiers = 5 | Development Status :: 3 - Alpha 6 | Intended Audience :: Science/Research 7 | Topic :: Scientific/Engineering :: Image Recognition 8 | License :: OSI Approved :: Apache Software License 9 | Programming Language :: Python :: 3.7 10 | Programming Language :: Python :: 3.8 11 | Programming Language :: Python :: 3.9 12 | description = dMRIPrep is a robust and easy-to-use pipeline for preprocessing of diverse dMRI data. 13 | license = Apache License, Version 2.0 14 | long_description = file:README.rst 15 | long_description_content_type = text/x-rst; charset=UTF-8 16 | name = dmriprep 17 | project_urls = 18 | Home = https://github.com/nipreps/dmriprep 19 | Documentation = https://www.nipreps.org/dmriprep 20 | 21 | [options] 22 | python_requires = >=3.7 23 | install_requires = 24 | dipy >=1.0.0 25 | h5py 26 | indexed_gzip >=0.8.8 27 | nibabel ~= 3.0 28 | nipype >= 1.5.1, < 2.0 29 | niworkflows >= 1.4.0rc6, <1.5 30 | numpy 31 | pybids >= 0.11.1 32 | pyyaml 33 | sdcflows ~= 2.0.4 34 | smriprep >= 0.8.0rc2 35 | svgutils != 0.3.2 36 | templateflow ~= 0.6 37 | toml 38 | setup_requires = 39 | setuptools >= 40.8.0 40 | test_requires = 41 | codecov 42 | coverage 43 | pytest 44 | packages = find: 45 | 46 | [options.packages.find] 47 | exclude = 48 | *.tests 49 | 50 | [options.package_data] 51 | dmriprep = 52 | VERSION 53 | config/reports-spec.yml 54 | data/boilerplate.bib 55 | data/flirtsch/*.cnf 56 | data/tests/config.toml 57 | data/tests/THP/* 58 | data/tests/THP/sub-THP0005/anat/* 59 | data/tests/THP/sub-THP0005/dwi/* 60 | 61 | [options.exclude_package_data] 62 | * = tests 63 | 64 | [options.extras_require] 65 | datalad = datalad 66 | doc = 67 | nbsphinx 68 | packaging 69 | pydot >= 1.2.3 70 | pydotplus 71 | sphinx >=2.1.2,<3.0 72 | sphinx-argparse 73 | sphinx_rtd_theme 74 | sphinxcontrib-apidoc ~= 0.3.0 75 | sphinxcontrib-napoleon 76 | sphinxcontrib-versioning 77 | docs = 78 | %(doc)s 79 | duecredit = duecredit 80 | resmon = psutil >=5.4 81 | popylar = popylar >= 0.2 82 | style = 83 | flake8 >= 3.7.0 84 | test = 85 | pytest >= 4.4 86 | pytest-xdist >= 2.0 87 | pytest-cov >= 2.10.1 88 | pytest-env 89 | coverage 90 | tests = 91 | %(test)s 92 | all = 93 | %(datalad)s 94 | %(doc)s 95 | %(duecredit)s 96 | %(resmon)s 97 | %(popylar)s 98 | %(tests)s 99 | 100 | [options.entry_points] 101 | console_scripts = 102 | dmriprep=dmriprep.cli.run:main 103 | 104 | [versioneer] 105 | VCS = git 106 | style = pep440 107 | versionfile_source = dmriprep/_version.py 108 | versionfile_build = dmriprep/_version.py 109 | tag_prefix = 110 | parentdir_prefix = 111 | 112 | [flake8] 113 | max-line-length = 99 114 | doctests = False 115 | ignore = 116 | W503 117 | E231 118 | E203 119 | exclude = 120 | *build/ 121 | docs/sphinxext/ 122 | docs/tools/ 123 | per-file-ignores = 124 | **/__init__.py : F401 125 | docs/conf.py : E265 126 | 127 | [tool:pytest] 128 | norecursedirs = .git 129 | addopts = -vsx --doctest-modules 130 | doctest_optionflags = ALLOW_UNICODE NORMALIZE_WHITESPACE NUMBER 131 | env = 132 | PYTHONHASHSEED=0 133 | filterwarnings = 134 | ignore::DeprecationWarning 135 | ignore::PendingDeprecationWarning 136 | ignore:cmp not installed:UserWarning 137 | ignore:This has not been fully tested:UserWarning 138 | 139 | [coverage:run] 140 | concurrency = multiprocessing 141 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """dMRIPrep's setup script.""" 2 | import sys 3 | from setuptools import setup 4 | import versioneer 5 | 6 | if __name__ == "__main__": 7 | setupargs = { 8 | "version": versioneer.get_version(), 9 | "cmdclass": versioneer.get_cmdclass(), 10 | } 11 | if "bdist_wheel" in sys.argv: 12 | setupargs["setup_requires"] = ["setuptools >= 40.8", "wheel"] 13 | setup(**setupargs) 14 | --------------------------------------------------------------------------------