highspeed-masks.py 11 KB

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