rois2manuscript 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #!/usr/bin/python3
  2. import sys
  3. import os
  4. from tempfile import mkdtemp
  5. from os.path import join as _opj
  6. from os.path import abspath
  7. from shutil import rmtree
  8. from configparser import SafeConfigParser
  9. from subprocess import check_call
  10. import nibabel as nb
  11. from scipy.ndimage import label, center_of_mass, maximum_position
  12. import numpy as np
  13. def ijk2xyz(ijk, aff):
  14. tmp = np.ones(4, dtype=ijk.dtype)
  15. tmp[:3] = ijk
  16. tmp = np.dot(aff, tmp)
  17. return tmp[:3]
  18. cfg = SafeConfigParser()
  19. cfg.read(_opj('code', 'stats2rois.cfg'))
  20. #if not os.path.exists(_opj(subj_code, 'rois')):
  21. # os.makedirs(_opj(subj_code, 'rois'))
  22. # encode ROI IDs as powers of 2
  23. roi_ids = {
  24. 'rFFA': int('00000000001', base=2), # 1
  25. 'rOFA': int('00000000010', base=2), # 2
  26. 'rPPA': int('00000000100', base=2), # 4
  27. 'rEBA': int('00000001000', base=2), # 8
  28. 'rLOC': int('00000010000', base=2), # 16
  29. 'lFFA': int('00000100000', base=2), # 32
  30. 'lOFA': int('00001000000', base=2), # 64
  31. 'lPPA': int('00010000000', base=2), # 128
  32. 'lEBA': int('00100000000', base=2), # 256
  33. 'lLOC': int('01000000000', base=2), # 512
  34. 'VIS': int('10000000000', base=2), # 1024
  35. }
  36. def describe_roi(sub, roi, contrast, cluster):
  37. res = {}
  38. mask_fname = '%s_%i_mask.nii.gz' % (roi, cluster)
  39. mask_fullpath = _opj(sub, 'rois', mask_fname)
  40. mask_img = nb.load(mask_fullpath)
  41. stats_img = nb.load(os.path.join(
  42. sub,
  43. '2ndlvl.gfeat',
  44. 'cope%i.feat' % contrast,
  45. 'stats',
  46. 'zstat1.nii.gz'))
  47. mask_arr = mask_img.get_data() > 0
  48. stats = stats_img.get_data()[mask_arr]
  49. res['meanZ'] = stats.mean()
  50. res['maxZ'] = stats.max()
  51. res['medianZ'] = np.median(stats)
  52. res['nvoxels'] = np.sum(mask_arr)
  53. res['volume'] = np.sum(mask_arr) * np.prod(mask_img.get_header().get_zooms()) / 1000.
  54. # get info in MNI space
  55. tmplwarp_fname = abspath(_opj(
  56. 'src',
  57. 'tnt',
  58. sub,
  59. 'bold3Tp2',
  60. 'in_grpbold3Tp2',
  61. 'subj2tmpl_warp.nii.gz'))
  62. tmpl_fname = abspath(_opj(
  63. 'src',
  64. 'tnt',
  65. 'templates',
  66. 'grpbold3Tp2',
  67. 'brain.nii.gz'))
  68. wdir = mkdtemp(suffix='_roi2manuscript_%s' % sub)
  69. mni_mask_fname = _opj(wdir, 'mni_mask.nii.gz')
  70. check_call([
  71. 'applywarp',
  72. '--in=%s' % mask_fullpath,
  73. '--ref=%s' % tmpl_fname,
  74. '--warp=%s' % tmplwarp_fname,
  75. '--interp=nn',
  76. '--out=%s' % mni_mask_fname])
  77. mni_mask_img = nb.load(mni_mask_fname)
  78. res['peakZ_MNI'] = tuple(ijk2xyz(
  79. np.array(maximum_position(mni_mask_img.get_data())),
  80. mni_mask_img.get_affine()))
  81. res['CoM_MNI'] = tuple(ijk2xyz(
  82. np.array(center_of_mass(mni_mask_img.get_data())),
  83. mni_mask_img.get_affine()))
  84. rmtree(wdir)
  85. return res, mni_mask_img
  86. mni_affine = None
  87. roi_stats = {}
  88. for sub in cfg.sections():
  89. for roi in cfg.options(sub):
  90. if len(roi) > 3:
  91. roi_label = '%s%s' % (roi[0], roi[1:].upper())
  92. else:
  93. roi_label = roi.upper()
  94. cluster = cfg.get(sub, roi).split()
  95. contrast = int(cluster[0])
  96. if contrast in [2, 3, 4, 5, 6]:
  97. contrast_label = 'strict'
  98. elif contrast in [7, 8, 9]:
  99. contrast_label = 'relaxed'
  100. else:
  101. contrast_label = 'simple'
  102. thresh = float(cluster[1])
  103. for clust in cluster[2:]:
  104. p, mni_mask_img = describe_roi(sub, roi_label, contrast, int(clust))
  105. if mni_affine is None:
  106. mni_affine = mni_mask_img.get_affine()
  107. if not roi_label in roi_stats:
  108. roi_stats[roi_label] = mni_mask_img.get_data()
  109. else:
  110. roi_stats[roi_label] += mni_mask_img.get_data()
  111. # TODO make contrast something better than a numerical ID
  112. p.update(dict(sub=int(sub[4:]), roi=roi_label,
  113. contrast=contrast_label, thresh=thresh))
  114. print('{roi} & {sub} & {thresh:.2f} & {meanZ:.2f} & {medianZ:.2f} & {maxZ:.2f} & {volume:.2f} & {nvoxels} & {peakZ_MNI[0]:.1f} & {peakZ_MNI[1]:.1f} & {peakZ_MNI[2]:.1f} & {CoM_MNI[0]:.1f} & {CoM_MNI[1]:.1f} & {CoM_MNI[2]:.1f} & {contrast} \\\\'.format(**p))
  115. for roi in roi_stats:
  116. nb.save(nb.Nifti1Image(roi_stats[roi].astype('int16'), mni_affine),
  117. '%s_overlap.nii.gz' % roi)