highspeed-masks.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # ======================================================================
  4. # SCRIPT INFORMATION:
  5. # ======================================================================
  6. # SCRIPT: CREATE BINARIZED MASKS FROM SEGMENTED FUNCTIONAL IMAGES
  7. # PROJECT: HIGHSPEED
  8. # WRITTEN BY LENNART WITTKUHN, 2018 - 2020
  9. # CONTACT: WITTKUHN AT MPIB HYPHEN BERLIN DOT MPG DOT DE
  10. # MAX PLANCK RESEARCH GROUP NEUROCODE
  11. # MAX PLANCK INSTITUTE FOR HUMAN DEVELOPMENT
  12. # MAX PLANCK UCL CENTRE FOR COMPUTATIONAL PSYCHIATRY AND AGEING RESEARCH
  13. # LENTZEALLEE 94, 14195 BERLIN, GERMANY
  14. # ======================================================================
  15. # IMPORT RELEVANT PACKAGES
  16. # ======================================================================
  17. # import basic libraries:
  18. import os
  19. import sys
  20. import warnings
  21. from os.path import join as opj
  22. # import nipype libraries:
  23. from nipype.interfaces.utility import IdentityInterface
  24. from nipype.interfaces.io import SelectFiles, DataSink
  25. from nipype.pipeline.engine import Workflow, Node, MapNode
  26. # import libraries for bids interaction:
  27. from bids.layout import BIDSLayout
  28. # import freesurfer interfaces:
  29. from nipype.interfaces.freesurfer import Binarize
  30. # import fsl interfaces:
  31. from niflow.nipype1.workflows.fmri.fsl import create_susan_smooth
  32. import datalad.api as dl
  33. # ======================================================================
  34. # ENVIRONMENT SETTINGS (DEALING WITH ERRORS AND WARNINGS):
  35. # ======================================================================
  36. # set the fsl output type environment variable:
  37. os.environ['FSLOUTPUTTYPE'] = 'NIFTI_GZ'
  38. # set the freesurfer subject directory:
  39. os.environ['SUBJECTS_DIR'] = '/home/mpib/wittkuhn/tools/freesurfer/'
  40. # deal with nipype-related warnings:
  41. os.environ['TF_CPP_MIN_LOG_LEVEL'] = "3"
  42. # filter out warnings related to the numpy package:
  43. warnings.filterwarnings("ignore", message="numpy.dtype size changed*")
  44. warnings.filterwarnings("ignore", message="numpy.ufunc size changed*")
  45. # ======================================================================
  46. # DEFINE PBS CLUSTER JOB TEMPLATE (NEEDED WHEN RUNNING ON THE CLUSTER):
  47. # ======================================================================
  48. job_template = """
  49. #PBS -l walltime=5:00:00
  50. #PBS -j oe
  51. #PBS -o /home/mpib/wittkuhn/highspeed/highspeed-masks/logs
  52. #PBS -m n
  53. #PBS -v FSLOUTPUTTYPE=NIFTI_GZ
  54. source /etc/bash_completion.d/virtualenvwrapper
  55. workon highspeed-masks
  56. module load fsl/5.0
  57. module load freesurfer/6.0.0
  58. """
  59. # ======================================================================
  60. # DEFINE PATHS AND SUBJECTS
  61. # ======================================================================
  62. path_root = None
  63. sub_list = None
  64. # path to the project root:
  65. project_name = 'highspeed-masks'
  66. path_root = os.getcwd().split(project_name)[0] + project_name
  67. # grab the list of subjects from the bids data set:
  68. layout = BIDSLayout(opj(path_root, 'bids'))
  69. # get all subject ids:
  70. sub_list = sorted(layout.get_subjects())
  71. # create a template to add the "sub-" prefix to the ids
  72. sub_template = ['sub-'] * len(sub_list)
  73. # add the prefix to all ids:
  74. sub_list = ["%s%s" % t for t in zip(sub_template, sub_list)]
  75. # if user defined to run specific subject
  76. sub_list = sub_list[int(sys.argv[1]):int(sys.argv[2])]
  77. # ======================================================================
  78. # DEFINE NODE: INFOSOURCE
  79. # ======================================================================
  80. # define the infosource node that collects the data:
  81. infosource = Node(IdentityInterface(
  82. fields=['subject_id']), name='infosource')
  83. # let the node iterate (parallelize) over all subjects:
  84. infosource.iterables = [('subject_id', sub_list)]
  85. # ======================================================================
  86. # DEFINE SELECTFILES NODE
  87. # ======================================================================
  88. path_func = opj(
  89. path_root, 'fmriprep', '*', '*',
  90. 'func', '*space-T1w*preproc_bold.nii.gz')
  91. path_func_parc = opj(
  92. path_root, 'fmriprep', '*',
  93. '*', 'func', '*space-T1w*aparcaseg_dseg.nii.gz')
  94. path_wholemask = opj(
  95. path_root, 'fmriprep', '*',
  96. '*', 'func', '*space-T1w*brain_mask.nii.gz')
  97. dl.get(path_func)
  98. dl.get(path_func_parc)
  99. dl.get(path_wholemask)
  100. # define all relevant files paths:
  101. templates = dict(
  102. func=opj(path_root, 'fmriprep', '{subject_id}', '*',
  103. 'func', '*space-T1w*preproc_bold.nii.gz'),
  104. func_parc=opj(path_root, 'fmriprep', '{subject_id}',
  105. '*', 'func', '*space-T1w*aparcaseg_dseg.nii.gz'),
  106. wholemask=opj(path_root, 'fmriprep', '{subject_id}',
  107. '*', 'func', '*space-T1w*brain_mask.nii.gz'),
  108. )
  109. # define the selectfiles node:
  110. selectfiles = Node(SelectFiles(templates, sort_filelist=True),
  111. name='selectfiles')
  112. # set expected thread and memory usage for the node:
  113. selectfiles.interface.num_threads = 1
  114. selectfiles.interface.estimated_memory_gb = 0.1
  115. # selectfiles.inputs.subject_id = 'sub-20'
  116. # selectfiles_results = selectfiles.run()
  117. # ======================================================================
  118. # DEFINE CREATE_SUSAN_SMOOTH WORKFLOW NODE
  119. # ======================================================================
  120. # define the susan smoothing node and specify the smoothing fwhm:
  121. susan = create_susan_smooth()
  122. # set the smoothing kernel:
  123. susan.inputs.inputnode.fwhm = 4
  124. # set expected thread and memory usage for the nodes:
  125. susan.get_node('inputnode').interface.num_threads = 1
  126. susan.get_node('inputnode').interface.estimated_memory_gb = 0.1
  127. susan.get_node('median').interface.num_threads = 1
  128. susan.get_node('median').interface.estimated_memory_gb = 3
  129. susan.get_node('mask').interface.num_threads = 1
  130. susan.get_node('mask').interface.estimated_memory_gb = 3
  131. susan.get_node('meanfunc2').interface.num_threads = 1
  132. susan.get_node('meanfunc2').interface.estimated_memory_gb = 3
  133. susan.get_node('merge').interface.num_threads = 1
  134. susan.get_node('merge').interface.estimated_memory_gb = 3
  135. susan.get_node('multi_inputs').interface.num_threads = 1
  136. susan.get_node('multi_inputs').interface.estimated_memory_gb = 3
  137. susan.get_node('smooth').interface.num_threads = 1
  138. susan.get_node('smooth').interface.estimated_memory_gb = 3
  139. susan.get_node('outputnode').interface.num_threads = 1
  140. susan.get_node('outputnode').interface.estimated_memory_gb = 0.1
  141. # ======================================================================
  142. # DEFINE BINARIZE NODE
  143. # ======================================================================
  144. mask_visual_labels = [
  145. 1005, 2005, # cuneus
  146. 1011, 2011, # lateral occipital
  147. 1021, 2021, # pericalcarine
  148. 1029, 2029, # superioparietal
  149. 1013, 2013, # lingual
  150. 1008, 2008, # inferioparietal
  151. 1007, 2007, # fusiform
  152. 1009, 2009, # inferiotemporal
  153. 1016, 2016, # parahippocampal
  154. 1015, 2015, # middle temporal
  155. ]
  156. mask_hippocampus_labels = [
  157. 17, 53, # left and right hippocampus
  158. ]
  159. mask_mtl_labels = [
  160. 17, 53, # left and right hippocampus
  161. 1016, 2016, # parahippocampal
  162. 1006, 2006, # ctx-entorhinal
  163. ]
  164. # function: use freesurfer mri_binarize to threshold an input volume
  165. mask_visual = MapNode(
  166. interface=Binarize(), name='mask_visual', iterfield=['in_file'])
  167. # input: match instead of threshold
  168. mask_visual.inputs.match = mask_visual_labels
  169. # optimize the efficiency of the node:
  170. mask_visual.plugin_args = {'qsub_args': '-l nodes=1:ppn=1', 'overwrite': True}
  171. mask_visual.plugin_args = {'qsub_args': '-l mem=100MB', 'overwrite': True}
  172. # function: use freesurfer mri_binarize to threshold an input volume
  173. mask_hippocampus = MapNode(
  174. interface=Binarize(), name='mask_hippocampus', iterfield=['in_file'])
  175. # input: match instead of threshold
  176. mask_hippocampus.inputs.match = mask_hippocampus_labels
  177. # optimize the efficiency of the node:
  178. mask_hippocampus.plugin_args = {
  179. 'qsub_args': '-l nodes=1:ppn=1', 'overwrite': True}
  180. mask_hippocampus.plugin_args = {
  181. 'qsub_args': '-l mem=100MB', 'overwrite': True}
  182. # function: use freesurfer mri_binarize to threshold an input volume
  183. mask_mtl = MapNode(
  184. interface=Binarize(), name='mask_mtl', iterfield=['in_file'])
  185. # input: match instead of threshold
  186. mask_mtl.inputs.match = mask_mtl_labels
  187. # optimize the efficiency of the node:
  188. mask_mtl.plugin_args = {'qsub_args': '-l nodes=1:ppn=1', 'overwrite': True}
  189. mask_mtl.plugin_args = {'qsub_args': '-l mem=100MB', 'overwrite': True}
  190. # ======================================================================
  191. # CREATE DATASINK NODE
  192. # ======================================================================
  193. # create a node of the function:
  194. datasink = Node(DataSink(), name='datasink')
  195. # assign the path to the base directory:
  196. datasink.inputs.base_directory = opj(path_root, 'masks')
  197. # create a list of substitutions to adjust the filepaths of datasink:
  198. substitutions = [('_subject_id_', '')]
  199. # assign the substitutions to the datasink command:
  200. datasink.inputs.substitutions = substitutions
  201. # determine whether to store output in parameterized form:
  202. datasink.inputs.parameterization = True
  203. # set expected thread and memory usage for the node:
  204. datasink.interface.num_threads = 1
  205. datasink.interface.estimated_memory_gb = 0.2
  206. # ======================================================================
  207. # DEFINE WORKFLOW PIPELINE:
  208. # ======================================================================
  209. # initiation of the 1st-level analysis workflow:
  210. wf = Workflow(name='masks')
  211. # stop execution of the workflow if an error is encountered:
  212. wf.config = {'execution': {'stop_on_first_crash': True}}
  213. # define the base directory of the workflow:
  214. wf.base_dir = opj(path_root, 'work')
  215. # connect infosource to selectfiles node:
  216. wf.connect(infosource, 'subject_id', selectfiles, 'subject_id')
  217. # connect functional files to smoothing workflow:
  218. wf.connect(selectfiles, 'func', susan, 'inputnode.in_files')
  219. wf.connect(selectfiles, 'wholemask', susan, 'inputnode.mask_file')
  220. wf.connect(susan, 'outputnode.smoothed_files', datasink, 'smooth')
  221. # connect segmented functional files to visual mask node
  222. wf.connect(selectfiles, 'func_parc', mask_visual, 'in_file')
  223. wf.connect(mask_visual, 'binary_file', datasink, 'mask_visual.@binary')
  224. wf.connect(mask_visual, 'count_file', datasink, 'mask_visual.@count')
  225. # connect segmented functional files to hippocampus node
  226. wf.connect(selectfiles, 'func_parc', mask_hippocampus, 'in_file')
  227. wf.connect(mask_hippocampus, 'binary_file', datasink, 'mask_hippocampus.@binary')
  228. wf.connect(mask_hippocampus, 'count_file', datasink, 'mask_hippocampus.@count')
  229. # connect segmented functional files to mtl node
  230. wf.connect(selectfiles, 'func_parc', mask_mtl, 'in_file')
  231. wf.connect(mask_mtl, 'binary_file', datasink, 'mask_mtl.@binary')
  232. wf.connect(mask_mtl, 'count_file', datasink, 'mask_mtl.@count')
  233. # ======================================================================
  234. # WRITE GRAPH AND EXECUTE THE WORKFLOW
  235. # ======================================================================
  236. # write the graph:
  237. wf.write_graph(graph2use='colored', simple_form=True)
  238. # set the maximum resources the workflow can utilize:
  239. # args_dict = {'status_callback' : log_nodes_cb}
  240. # execute the workflow depending on the operating system:
  241. if 'darwin' in sys.platform:
  242. # will execute the workflow using all available cpus:
  243. wf.run(plugin='MultiProc')
  244. elif 'linux' in sys.platform:
  245. wf.run(plugin='PBS', plugin_args=dict(template=job_template))