stats2rois 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. subj = int(sys.argv[1])
  21. subj_code = 'sub-%.2i' % subj
  22. mode = None
  23. rois = None
  24. if len(sys.argv) > 2:
  25. mode = 'indi'
  26. rois = sys.argv[2:]
  27. if not os.path.exists(_opj(subj_code, 'rois')):
  28. os.makedirs(_opj(subj_code, 'rois'))
  29. # encode ROI IDs as powers of 2
  30. roi_ids = {
  31. 'rFFA': int('0000000000001', base=2), # 1
  32. 'rOFA': int('0000000000010', base=2), # 2
  33. 'rpSTS': int('0000000000100', base=2), # 4
  34. 'rPPA': int('0000000001000', base=2), # 8
  35. 'rEBA': int('0000000010000', base=2), # 16
  36. 'rLOC': int('0000000100000', base=2), # 32
  37. 'lFFA': int('0000001000000', base=2), # 64
  38. 'lOFA': int('0000010000000', base=2), # 128
  39. 'lpSTS': int('0000100000000', base=2), # 256
  40. 'lPPA': int('0001000000000', base=2), # 512
  41. 'lEBA': int('0010000000000', base=2), # 1024
  42. 'lLOC': int('0100000000000', base=2), # 2048
  43. 'VIS': int('1000000000000', base=2), # 4096
  44. }
  45. #mask_fname = abspath(_opj(
  46. # # TODO: switch back before release
  47. # #'src',
  48. # '..',
  49. # 'tnt',
  50. # subj_code,
  51. # 'bold3Tp2',
  52. # 'brain_mask.nii.gz'))
  53. tmplwarp_fname = abspath(_opj(
  54. # TODO: switch back before release
  55. #'src',
  56. '..',
  57. 'tnt',
  58. subj_code,
  59. 'bold3Tp2',
  60. 'in_grpbold3Tp2',
  61. 'subj2tmpl_warp.nii.gz'))
  62. tmpl_fname = abspath(_opj(
  63. # TODO: switch back before release
  64. #'src',
  65. '..',
  66. 'tnt',
  67. 'templates',
  68. 'grpbold3Tp2',
  69. 'brain.nii.gz'))
  70. roisummary_vol = None
  71. roisummary_hdr = None
  72. if rois is None:
  73. rois = cfg.options(subj_code)
  74. for roi in rois:
  75. # doesn't do casing properly
  76. roi = roi[:-3] + roi[-3:].upper()
  77. print(roi)
  78. wdir = mkdtemp(suffix='_stats2roi_%s' % subj_code)
  79. #wdir = '/tmp/stats2rois'
  80. #print(wdir)
  81. # get particular setting for this ROI
  82. info = cfg.get(subj_code, roi).split()
  83. cope = int(info[0])
  84. zthresh = float(info[1])
  85. clabels = info[2:]
  86. cluster_idx = [int(i) for i in info[2:]]
  87. zstats_fname = abspath(_opj(
  88. subj_code,
  89. '2ndlvl_z164.gfeat',
  90. 'cope%i.feat' % cope,
  91. 'thresh_zstat1.nii.gz'))
  92. grp_zstats_fname = _opj(wdir, 'grpzstats.nii.gz')
  93. # find clusters
  94. zstat_img = nb.load(zstats_fname)
  95. clusters, nclusters = label(zstat_img.get_data() > zthresh)
  96. #print('Cluster sizes:', {i: np.sum(clusters == i) for i in range(1, nclusters + 1)})
  97. # make ROI mask
  98. roi_fname = _opj(subj_code, 'rois', '%s.nii.gz' % roi)
  99. roi_vol = np.zeros(clusters.shape, dtype=np.bool)
  100. for cli in cluster_idx:
  101. roi_vol += clusters == cli
  102. if roisummary_vol is None:
  103. roisummary_vol = np.zeros(roi_vol.shape, dtype='int16')
  104. roisummary_hdr = zstat_img.get_header()
  105. roisummary_vol += roi_vol.astype(int) * roi_ids[roi]
  106. # save the ROI mask file
  107. roi_img = nb.Nifti1Image(roi_vol.astype('int16'), zstat_img.get_affine())
  108. nb.save(roi_img, roi_fname)
  109. with open(_opj(subj_code, 'rois', '%s_info.txt' % roi), 'w') as infofile:
  110. # native space info
  111. infofile.write("nvoxels (native): %i\n" % np.sum(roi_vol > 0))
  112. infofile.write(
  113. "volume (native cm^3): %f\n"
  114. % (np.sum(roi_vol > 0) * np.prod(roi_img.get_header().get_zooms()) / 1000.))
  115. roi_stats = zstat_img.get_data()[roi_vol > 0]
  116. infofile.write(
  117. 'Z-stats (min, mean, median, max): %.2f, %.2f, %.2f, %.2f\n'
  118. % (roi_stats.min(), roi_stats.mean(), np.median(roi_stats), roi_stats.max()))
  119. roi_stats = zstat_img.get_data() * (roi_vol > 0)
  120. infofile.write("peak Z (native IJK): %i, %i, %i\n" % maximum_position(roi_stats))
  121. infofile.write("CoM (native IJK): %i, %i, %i\n" % center_of_mass(roi_stats))
  122. # MNI space info
  123. roi_stats_fname = _opj(wdir, 'roi_stats.nii.gz')
  124. nb.save(nb.Nifti1Image(roi_stats, zstat_img.get_affine()), roi_stats_fname)
  125. grp_stats_fname = _opj(wdir, 'grp_stats.nii.gz')
  126. check_call([
  127. 'applywarp',
  128. '--in=%s' % roi_stats_fname,
  129. '--ref=%s' % tmpl_fname,
  130. '--warp=%s' % tmplwarp_fname,
  131. '--interp=trilinear',
  132. '--out=%s' % grp_stats_fname])
  133. grp_stats_img = nb.load(grp_stats_fname)
  134. infofile.write(
  135. "peak Z (MNI mm): %.2f, %.1f, %.1f\n"
  136. % tuple(
  137. ijk2xyz(
  138. np.array(maximum_position(grp_stats_img.get_data())),
  139. grp_stats_img.get_affine())))
  140. infofile.write(
  141. "CoM (MNI mm): %.2f, %.1f, %.1f\n"
  142. % tuple(
  143. ijk2xyz(
  144. np.array(center_of_mass(grp_stats_img.get_data())),
  145. grp_stats_img.get_affine())))
  146. native_clusters_fname = _opj(wdir, 'native_clusters.nii.gz')
  147. nb.save(zstat_img, _opj(wdir, 'stats.nii.gz'))
  148. grp_roi_fname = _opj(wdir, 'grp_roi.nii.gz')
  149. grp_clusters_fname = _opj(wdir, 'grp_clusters.nii.gz')
  150. nb.save(nb.Nifti1Image(clusters, zstat_img.get_affine()),
  151. native_clusters_fname)
  152. #check_call(
  153. # ['easythresh', zstats_fname, mask_fname, '%s' % zthresh, '%s' % pthresh,
  154. # mask_fname, 'stats2rois'],
  155. # cwd=wdir)
  156. if mode == 'indi':
  157. check_call([
  158. 'applywarp',
  159. '--in=%s' % roi_fname,
  160. '--ref=%s' % tmpl_fname,
  161. '--warp=%s' % tmplwarp_fname,
  162. '--interp=nn',
  163. '--out=%s' % grp_roi_fname])
  164. check_call([
  165. 'applywarp',
  166. '--in=%s' % native_clusters_fname,
  167. '--ref=%s' % tmpl_fname,
  168. '--warp=%s' % tmplwarp_fname,
  169. '--interp=nn',
  170. '--out=%s' % grp_clusters_fname])
  171. check_call([
  172. 'applywarp',
  173. '--in=%s' % zstats_fname,
  174. '--ref=%s' % tmpl_fname,
  175. '--warp=%s' % tmplwarp_fname,
  176. '--interp=trilinear',
  177. '--out=%s' % grp_zstats_fname])
  178. check_call([
  179. 'fslview',
  180. '../tnt/templates/grpbold3Tp2/from_mni/MNI152_T1_1mm.nii.gz',
  181. grp_zstats_fname,
  182. '-l', 'Red-Yellow',
  183. grp_clusters_fname,
  184. '-l',
  185. 'Random-Rainbow',
  186. grp_roi_fname,
  187. '-l',
  188. 'Greyscale',
  189. ])
  190. rmtree(wdir)
  191. if mode == 'indi':
  192. sys.exit(0)
  193. roisummary_fname = _opj(subj_code, 'rois', 'summary.nii.gz')
  194. nb.save(nb.Nifti1Image(roisummary_vol, roisummary_hdr.get_best_affine()),
  195. roisummary_fname)
  196. #grp_roisummary_fname = 'grp_roisummary.nii.gz'
  197. #check_call([
  198. # 'applywarp',
  199. # '--in=%s' % roisummary_fname,
  200. # '--ref=%s' % tmpl_fname,
  201. # '--warp=%s' % tmplwarp_fname,
  202. # '--interp=nn',
  203. # '--out=%s' % grp_roisummary_fname])
  204. #check_call([
  205. # 'fslview',
  206. # '../tnt/templates/grpbold3Tp2/from_mni/MNI152_T1_1mm.nii.gz',
  207. # grp_roisummary_fname,
  208. # '-l',
  209. # 'Random-Rainbow',
  210. #])