afni_allin_slices.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #!/usr/bin/env python3
  2. # Based on mc-afni-v4.2.2.sh.
  3. import subprocess as sp
  4. import argparse
  5. import os
  6. import pdb
  7. from collections import deque
  8. from nipype.interfaces.base import (
  9. TraitedSpec,
  10. CommandLineInputSpec,
  11. CommandLine,
  12. Directory,
  13. File,
  14. isdefined,
  15. )
  16. import traits.api as traits
  17. def defined_in(key, d):
  18. return key in d.keys() and d[key] is not None
  19. def print_run(cmd, args=None):
  20. print('cmd=' + cmd)
  21. if args is not None:
  22. print('args=' + str(args))
  23. cmd = cmd % args
  24. sp.check_call(cmd, shell=True)
  25. def nii_val(nii_file, var):
  26. val = sp.check_output(
  27. ['fslval', nii_file, var]).decode('UTF-8').strip()
  28. return val
  29. def register(**kwargs):
  30. if not defined_in('tmpdir', kwargs):
  31. # defined for NiPype
  32. kwargs['tmpdir'] = os.getcwd()
  33. print(kwargs['tmpdir'])
  34. if not defined_in('out_init_mc', kwargs):
  35. kwargs['out_init_mc'] = os.path.join(
  36. "%(tmpdir)s" % kwargs,
  37. 'func_3dAllineate.nii.gz')
  38. if not defined_in('TR', kwargs):
  39. kwargs['TR'] = nii_val(kwargs['func'], 'pixdim4')
  40. print('TR = %(TR)s' % kwargs)
  41. if not defined_in('1Dparam_save', kwargs):
  42. kwargs['1Dparam_save'] = 'reg'
  43. if not defined_in('1Dmatrix_save', kwargs):
  44. kwargs['1Dmatrix_save'] = 'reg'
  45. if not defined_in('ref', kwargs):
  46. kwargs['ref'] = '%(tmpdir)s/func_median.nii.gz' % kwargs
  47. if not os.path.isfile(kwargs['ref']):
  48. print_run('fslmaths %(func)s -Tmedian %(ref)s' % kwargs)
  49. if not os.path.isfile(kwargs['out_init_mc']):
  50. print("\nIn: %s" % os.getcwd())
  51. print("Did not find %(out_init_mc)s." % kwargs)
  52. print_run(
  53. '3dAllineate -weight "%(weights)s" '
  54. '-base %(ref)s '
  55. '-source "%(func)s" '
  56. '-prefix "%(out_init_mc)s" '
  57. '-1Dparam_save %(1Dparam_save)s '
  58. '-1Dmatrix_save %(1Dmatrix_save)s ',
  59. kwargs)
  60. sp.check_call('fslslice "%(ref)s" "%(tmpdir)s/ref"' % kwargs, shell=True)
  61. sp.check_call(
  62. 'fslslice "%(weights)s" "%(tmpdir)s/weights"' %
  63. kwargs, shell=True)
  64. sp.check_call(
  65. 'fslsplit "%(out_init_mc)s" "%(tmpdir)s/func_time_"' % kwargs,
  66. shell=True)
  67. processes = deque()
  68. for t in range(1000):
  69. print('vol=%d' % t)
  70. t_args = kwargs.copy()
  71. t_args['t'] = t
  72. t_args['func_time'] = "%(tmpdir)s/func_time_%(t)04d" % (t_args)
  73. t_args['dest_prefix'] = "%(tmpdir)s/reg_time_%(t)04d" % t_args
  74. if os.path.isfile('%(dest_prefix)s+orig.BRIK' % t_args):
  75. continue
  76. if not os.path.isfile('%(func_time)s.nii.gz' % t_args):
  77. print('image %(func_time)s.nii.gz does not exist!' % t_args)
  78. break
  79. sp.check_call(
  80. 'fslslice "%(func_time)s" "%(func_time)s"' %
  81. t_args, shell=True)
  82. for i in range(1000):
  83. i_args = t_args.copy()
  84. i_args['i'] = i
  85. # input files
  86. i_args['func'] = "%(func_time)s_slice_%(i)04d.nii.gz" % i_args
  87. i_args['w'] = "%(tmpdir)s/weights_slice_%(i)04d.nii.gz" % i_args
  88. i_args['ref'] = "%(tmpdir)s/ref_slice_%(i)04d.nii.gz" % i_args
  89. # output files
  90. i_args['destti'] = (
  91. "%(tmpdir)s/reg_time_%(t)04d_slice_%(i)04d.nii.gz" % i_args)
  92. if not os.path.isfile(i_args['func']):
  93. print('func %(func)s does not exist' % i_args)
  94. break
  95. if not os.path.isfile(i_args['w']):
  96. print('w %(w)s does not exist' % i_args)
  97. break
  98. if not os.path.isfile(i_args['ref']):
  99. print('ref %(ref)s does not exist' % i_args)
  100. break
  101. # print('Running 3dAllineate for slice %(i)d ' % i_args)
  102. if not os.path.isfile(i_args['destti']):
  103. cmd = [
  104. '3dAllineate',
  105. '-onepass',
  106. '-nwarp', i_args['nwarp'],
  107. '-fineblur', '%0.2f' % i_args['fineblur'],
  108. '-weight', i_args['w'],
  109. '-base', i_args['ref'],
  110. '-source', i_args['func'],
  111. '-prefix', i_args['destti']]
  112. print(' '.join(cmd))
  113. processes.append(
  114. sp.Popen(cmd))
  115. if len(processes) > 10: # limit parallelism
  116. processes.popleft().wait()
  117. while len(processes) > 0:
  118. processes.popleft().wait()
  119. # Delete temporary files
  120. # rm ${func_time}_slice_????.nii.gz
  121. #if not os.path.isfile('%(dest_prefix)s+orig.BRIK' % t_args):
  122. print_run(
  123. "3dZcat -prefix %(dest_prefix)s "
  124. "%(dest_prefix)s_slice_????.nii.gz",
  125. t_args)
  126. # rm ${dest_prefix}_slice_????.nii.gz
  127. print_run("rm %(dest_prefix)s_slice_????.nii.gz" % t_args)
  128. print_run("rm %(tmpdir)s/ref_slice_????.nii.gz", kwargs)
  129. print_run("rm %(tmpdir)s/func_time_????.nii.gz", kwargs)
  130. print_run("rm %(tmpdir)s/weights_slice_????.nii.gz", kwargs)
  131. kwargs['dest_prefix'] = "%(tmpdir)s/reg" % kwargs
  132. if not os.path.isfile("%(dest_prefix)s.nii" % kwargs):
  133. # print_run("3dZcat -prefix %(dest_prefix)s %(dest_prefix)s_"
  134. # "slice_????.nii.gz", kwargs)
  135. print_run("3dTcat -tr %(TR)s -prefix %(dest_prefix)s "
  136. "%(dest_prefix)s_time_????+orig.BRIK", kwargs)
  137. print_run("3dAFNItoNIFTI -prefix %(dest_prefix)s %(dest_prefix)s+orig",
  138. kwargs)
  139. if not os.path.isfile("%(out)s"):
  140. print_run("fslmaths %(dest_prefix)s.nii %(out)s", kwargs)
  141. # slices can be missing. always resample to ref grid.
  142. print_run("3dresample -input %(out)s -prefix %(out)s "
  143. "-master %(ref)s -overwrite", kwargs)
  144. print_run("3dresample -input %(dest_prefix)s.nii -prefix %(dest_prefix)s.nii "
  145. "-master %(ref)s -overwrite", kwargs)
  146. # rm $DEST/*.BRIK
  147. # rm $DEST/*.HEAD
  148. #
  149. print_run("fslmaths %(dest_prefix)s.nii -sub %(ref)s -abs "
  150. "-mas %(weights)s %(tmpdir)s/absdiff.nii.gz", kwargs)
  151. print_run("fslmaths %(tmpdir)s/absdiff.nii.gz -Tmean "
  152. "%(tmpdir)s/absdiff_mean.nii.gz", kwargs)
  153. if __name__ == '__main__':
  154. parser = argparse.ArgumentParser(
  155. description='Performs non-linear slice-by-slice registration '
  156. '(based on mc-afni-v4.2.2.sh).')
  157. parser.add_argument(
  158. '--func', '-i', required=True, type=str,
  159. help='Functional image with motion and field distortions.')
  160. parser.add_argument(
  161. '-o', '--out', required=True, type=str,
  162. help='Motion registered image.')
  163. parser.add_argument(
  164. '-r', '--ref', required=True, type=str,
  165. help='The reference / base functional to register to.')
  166. parser.add_argument(
  167. '--weights', required=True, type=str,
  168. help='The weights or mask used for registering.')
  169. parser.add_argument(
  170. '--tmpdir', type=str, help='Temporary directory.')
  171. parser.add_argument('--1Dparam_save', type=str)
  172. parser.add_argument('--1Dmatrix_save', type=str)
  173. parser.add_argument(
  174. '--out_init_mc', type=str,
  175. help='Initial motion registration (linear, not slice-by-slice).')
  176. parser.add_argument('--fineblur', type=float, default=0.5)
  177. parser.add_argument(
  178. '--nwarp', type=str, help='Motion registered image.',
  179. default='heptic',
  180. choices=['cubic', 'quintic', 'heptic', 'nonic'])
  181. args = parser.parse_args()
  182. register(**vars(args))
  183. class AFNIAllinSlicesInputSpec(CommandLineInputSpec):
  184. """ The inputspec specifies all the parameters of the command.
  185. """
  186. in_file = File(desc="Functional without motion correction",
  187. exists=True, mandatory=True,
  188. argstr="--func %s")
  189. # ref_file is generated as the median of the functional
  190. ref_file = File(
  191. desc="Reference / base image to register to",
  192. name_source=['in_file'],
  193. name_template="%s_Tmedian",
  194. keep_extension=True,
  195. argstr="--ref %s")
  196. in_weight_file = File(
  197. argstr='--weights %s',
  198. exists=True, mandatory=True,
  199. )
  200. working_dir = Directory(argstr='--tmpdir %s')
  201. fineblur = traits.Float(argstr='--fineblur %f')
  202. # --------------------------------------- Output / generated files --------
  203. out_file = File(
  204. argstr='--out %s',
  205. hash_files=False,
  206. name_source=['in_file'],
  207. name_template="%s_mc",
  208. keep_extension=True,
  209. )
  210. out_init_mc = File(
  211. argstr='--out_init_mc %s',
  212. hash_files=False,
  213. name_source=['in_file'],
  214. name_template="%s_prelim-mc",
  215. keep_extension=True,
  216. )
  217. oned_file = File(
  218. argstr='--1Dparam_save %s',
  219. hash_files=False,
  220. name_source=['in_file'],
  221. name_template="%s.param.1D",
  222. )
  223. oned_matrix_save = File(
  224. argstr='--1Dmatrix_save %s',
  225. hash_files=False,
  226. name_source=['in_file'],
  227. name_template="%s.aff12.1D",
  228. )
  229. class AFNIAllinSlicesOutputSpec(TraitedSpec):
  230. out_file = File(desc="Motion corrected / registered image", exists=True)
  231. out_init_mc = File(
  232. desc="Preliminary whole-brain motion correction",
  233. exists=True)
  234. oned_file = File(exists=True)
  235. oned_matrix_save = File(exists=True)
  236. ref_file = File(exists=True)
  237. class AFNIAllinSlices(CommandLine):
  238. """ NiPype wrapper """
  239. input_spec = AFNIAllinSlicesInputSpec
  240. output_spec = AFNIAllinSlicesOutputSpec
  241. _cmd = 'afni_allin_slices.py'
  242. # def run(self, updatehash=False):
  243. # import pdb
  244. # pdb.set_trace()
  245. # super(self, AFNIAllinSlices).run(updatehash=updatehash)