OSCR

Dog brain representations of human facial expressions: Encoding happiness and differentiating specific negative expressions.

Code ↔ Paper

5 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 5 matches
  1. [1] § STAR★Methods › Method details › Experiment 2 › Quantification and statistical analysis ↔ preprocess_functions.py, lines 1524–1578 · score 0.70 · binary mask, FSL, FD, displacement, rotation, motion
  2. [2] § STAR★Methods › Method details › Experiment 2 › Quantification and statistical analysis ↔ tools/fsf_design.py, lines 1–50 · score 0.65 · convolving, swapped, FSL, Tool, split, filtered
  3. [3] § STAR★Methods › Method details › Experiment 1 › Quantification and statistical analysis ↔ utils.py, lines 179–313 · score 0.57 · anatomical image, FSL, kernel, smoothing, preprocessing, fMRI
  4. [4] § Results › Experiment 1: Happy faces elicit stronger right temporal-caudate responses than neutral faces in dogs ↔ export_static.py, lines 240–315 · score 0.52 · Sylvian gyrus, brain response, caudal, temporal, radius, dog
  5. [5] § STAR★Methods › Method details › Experiment 1 › Quantification and statistical analysis ↔ convert_coords.ipynb, lines 394–457 · score 0.51 · Dog Brain Toolkit, FSL, smoothing, atlas, fMRI

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 1,594 lines · 72 KB · no license · 1 match

  1. import os
  2. import utils
  3. import shutil
  4. from nilearn.plotting import plot_anat, show
  5. import ipywidgets as widgets
  6. import utils
  7. import os
  8. from importlib import reload
  9. from nilearn import plotting
  10. from nilearn import image as nli
  11. import nibabel as nib
  12. import numpy as np
  13. from ipywidgets import HBox, VBox
  14. import numpy as np
  15. from nilearn.plotting import plot_anat, show
  16. import ipywidgets as widgets
  17. import matplotlib.pyplot as plt
  18. from matplotlib.colors import ListedColormap
  19. from IPython.display import display, Markdown, clear_output
  20. # Description: Functions to preprocess fMRI data using FSL
  21. # Author: Raul Hernandez
  22. def bet_app(project_dict, sub_N, initial_params=None):
  23. global data_mask1, data_mask2, data_mask3
  24. # Create a custom colormaps
  25. # black
  26. black = np.array([0, 0, 0, 0]) # RGBA for black (last value is alpha)
  27. # red
  28. red = np.array([1, 0, 0, 1]) # RGBA for red
  29. # blue
  30. blue = np.array([0, 0, 1, 1]) # RGBA for blue
  31. # yellow
  32. yellow = np.array([1, 1, 0, 1]) # RGBA for yellow (last value is alpha)
  33. # create a red colormap
  34. red_cmap = ListedColormap([black, red])
  35. # create a blue colormap
  36. blue_cmap = ListedColormap([black, blue])
  37. # create a yellow colormap
  38. yellow_cmap = ListedColormap([black, yellow])
  39. dataset = project_dict['Dataset']
  40. session = project_dict['Session']
  41. task = project_dict['Task']
  42. specie = project_dict['Specie']
  43. datafolder = project_dict['Datafolder']
  44. # working directory
  45. workingdir = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  46. mean_fct_file = workingdir + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  47. # adding session if there is one
  48. if session != '':
  49. mean_fct_file += '_ses-' + session
  50. cut_mean_fct_file = mean_fct_file + '_task-' + task + '_mean_fct.nii.gz'
  51. mask_file = mean_fct_file + '_task-' + task + '_mean_fct_mask.nii.gz'
  52. mask_file1 = mean_fct_file + '_task-' + task + '_mean_fct_mask1.nii.gz'
  53. mask_file2 = mean_fct_file + '_task-' + task + '_mean_fct_mask2.nii.gz'
  54. mask_file3 = mean_fct_file + '_task-' + task + '_mean_fct_mask3.nii.gz'
  55. mean_fct_file += '_task-' + task + '_mean_fct.nii.gz'
  56. masked_mean_fct_file = mean_fct_file[:-7] + '_brain.nii.gz'
  57. # load cut_mean_fct_file
  58. img = nib.load(cut_mean_fct_file)
  59. # Get the data from the image
  60. data = img.get_fdata()
  61. # if the mask files exist, load them
  62. if os.path.exists(mask_file1):
  63. mask_img1 = nib.load(mask_file1)
  64. data_mask1 = mask_img1.get_fdata()
  65. mask_img2 = nib.load(mask_file2)
  66. data_mask2 = mask_img2.get_fdata()
  67. mask_img3 = nib.load(mask_file3)
  68. data_mask3 = mask_img3.get_fdata()
  69. #params_file = mean_fct_file[:-7] + '_bet_params.txt'
  70. #param_dict = utils.read_params_file(params_file)
  71. # determine min and max values for each axis
  72. maxX,maxY,maxZ = img.shape
  73. def plot_bet(x,y,z, betx_1, bety_1, betz_1, betx_2, bety_2, betz_2, betx_3, bety_3, betz_3, plot_mask):
  74. global data_mask1, data_mask2, data_mask3
  75. """
  76. Plot slices from the sagittal, coronal, and axial views side by side.
  77. """
  78. fig, axes = plt.subplots(1, 3, figsize=(8, 5))
  79. # Sagittal
  80. sagittal_slice = data[x, :, :]
  81. axes[0].imshow(sagittal_slice.T, cmap='gray', origin='lower')
  82. axes[0].scatter(bety_1,betz_1,s=200, c='red')
  83. # plot blue only if betx_2 matches the current slice, else plot it with alpha=0.4
  84. if betx_2 == x:
  85. axes[0].scatter(bety_2,betz_2,s=200, c='blue')
  86. else:
  87. axes[0].scatter(bety_2,betz_2,s=200, c='blue', alpha=0.4)
  88. axes[0].scatter(bety_3,betz_3,s=200, c='yellow')
  89. axes[0].axis('off')
  90. # display mask if plot_mask is True
  91. if plot_mask:
  92. # get mask1 for sagital slice
  93. sagital_mask1 = data_mask1[x, :, :]
  94. # plot mask1 in red
  95. axes[0].imshow(sagital_mask1.T, cmap=red_cmap, origin='lower', alpha=0.5)
  96. # get mask2 for sagital slice
  97. sagital_mask2 = data_mask2[x, :, :]
  98. # plot mask2 in blue
  99. axes[0].imshow(sagital_mask2.T, cmap=blue_cmap, origin='lower', alpha=0.5)
  100. # get mask3 for sagital slice
  101. sagital_mask3 = data_mask3[x, :, :]
  102. # plot mask3 in yellow
  103. axes[0].imshow(sagital_mask3.T, cmap=yellow_cmap, origin='lower', alpha=0.5)
  104. # Coronal
  105. coronal_slice = data[:, y, :]
  106. axes[1].imshow(coronal_slice.T, cmap='gray', origin='lower')
  107. # plot red only if bety_1 matches the current slice, else plot it with alpha=0.4
  108. if bety_1 == y:
  109. axes[1].scatter(betx_1,betz_1,s=200, c='red')
  110. else:
  111. axes[1].scatter(betx_1,betz_1,s=200, c='red', alpha=0.4)
  112. axes[1].scatter(betx_2,betz_2,s=200, c='blue')
  113. axes[1].scatter(betx_3,betz_3,s=200, c='yellow')
  114. axes[1].axis('off')
  115. # display mask if plot_mask is True
  116. if plot_mask:
  117. # get mask1 for coronal slice
  118. coronal_mask1 = data_mask1[:, y, :]
  119. # plot mask1 in red
  120. axes[1].imshow(coronal_mask1.T, cmap=red_cmap, origin='lower', alpha=0.5)
  121. # get mask2 for coronal slice
  122. coronal_mask2 = data_mask2[:, y, :]
  123. # plot mask2 in blue
  124. axes[1].imshow(coronal_mask2.T, cmap=blue_cmap, origin='lower', alpha=0.5)
  125. # get mask3 for coronal slice
  126. coronal_mask3 = data_mask3[:, y, :]
  127. # plot mask3 in yellow
  128. axes[1].imshow(coronal_mask3.T, cmap=yellow_cmap, origin='lower', alpha=0.5)
  129. # Axial
  130. axial_slice = data[:, :, z]
  131. axes[2].imshow(axial_slice.T, cmap='gray', origin='lower')
  132. axes[2].scatter(betx_1,bety_1,s=200, c='red')
  133. axes[2].scatter(betx_2,bety_2,s=200, c='blue')
  134. # plot yellow only if betz_3 matches the current slice, else plot it with alpha=0.4
  135. if betz_3 == z:
  136. axes[2].scatter(betx_3,bety_3,s=200, c='yellow')
  137. else:
  138. axes[2].scatter(betx_3,bety_3,s=200, c='yellow', alpha=0.4)
  139. axes[2].axis('off')
  140. # display mask if plot_mask is True
  141. if plot_mask:
  142. # get mask1 for axial slice
  143. axial_mask1 = data_mask1[:, :, z]
  144. # plot mask1 in red
  145. axes[2].imshow(axial_mask1.T, cmap=red_cmap, origin='lower', alpha=0.5)
  146. # get mask2 for axial slice
  147. axial_mask2 = data_mask2[:, :, z]
  148. # plot mask2 in blue
  149. axes[2].imshow(axial_mask2.T, cmap=blue_cmap, origin='lower', alpha=0.5)
  150. # get mask3 for axial slice
  151. axial_mask3 = data_mask3[:, :, z]
  152. # plot mask3 in yellow
  153. axes[2].imshow(axial_mask3.T, cmap=yellow_cmap, origin='lower', alpha=0.5)
  154. # make a tight layout
  155. plt.tight_layout()
  156. # make the background black
  157. fig.patch.set_facecolor('black')
  158. # display the plot
  159. plt.show()
  160. # define function to apply BET
  161. def apply_bet_button(betx_1, bety_1, betz_1, thr_1, betx_2, bety_2, betz_2, thr_2, betx_3, bety_3, betz_3, thr_3):
  162. global data_mask1, data_mask2, data_mask3
  163. # determine name of params_file based on mean_fct_file
  164. params_file = mean_fct_file[:-7] + '_bet_params.txt'
  165. # create a dict with the parameters
  166. param_dict = {'betx_1':betx_1, 'bety_1':bety_1, 'betz_1':betz_1, 'thr_1':thr_1,
  167. 'betx_2':betx_2, 'bety_2':bety_2, 'betz_2':betz_2, 'thr_2':thr_2,
  168. 'betx_3':betx_3, 'bety_3':bety_3, 'betz_3':betz_3, 'thr_3':thr_3,
  169. 'output_file':mask_file, 'masked_file':masked_mean_fct_file,
  170. 'mask_file1':mask_file1, 'mask_file2':mask_file2, 'mask_file3':mask_file3,
  171. }
  172. # save the cutting parameters
  173. utils.write_params_file(params_file, param_dict)
  174. print('button pressed')
  175. command = f"./run_bet.sh {cut_mean_fct_file} {params_file}"
  176. os.system(command)
  177. # update button description and status
  178. col1.children[3].description = 'Display mask'
  179. col1.children[3].disabled = False
  180. # Load each of the three masks
  181. mask_img1 = nib.load(mask_file1)
  182. data_mask1 = mask_img1.get_fdata()
  183. mask_img2 = nib.load(mask_file2)
  184. data_mask2 = mask_img2.get_fdata()
  185. mask_img3 = nib.load(mask_file3)
  186. data_mask3 = mask_img3.get_fdata()
  187. print('Done')
  188. # create 4 sets of sliders, one for the slice, and 3 for the spheres marking the initial place of the BET sphere
  189. # check if mask file 1 already exist
  190. if os.path.exists(mask_file1):
  191. button_description = 'Display mask'
  192. button_disabled = False
  193. else:
  194. button_description = 'Mask not available'
  195. button_disabled = True
  196. # sliders for the slice
  197. col1 = widgets.VBox([widgets.IntSlider(min=0, max=maxX, step=1, value=np.round(maxX/2), description='X'),
  198. widgets.IntSlider(min=0, max=maxY, step=1, value=np.round(maxY/2), description='Y'),
  199. widgets.IntSlider(min=0, max=maxZ, step=1, value=np.round(maxZ/2), description='Z'),
  200. widgets.ToggleButton(value=False, description=button_description, button_style='info', disabled=button_disabled),
  201. ])
  202. # check if param_dict exists if yes, load it
  203. if os.path.exists(mean_fct_file[:-7] + '_bet_params.txt'):
  204. param_dict = utils.read_params_file(mean_fct_file[:-7] + '_bet_params.txt')
  205. else:
  206. if initial_params is not None:
  207. param_dict = {'betx_1':np.round(maxX/2),
  208. 'bety_1':initial_params['bety_1'],
  209. 'betz_1':initial_params['betz_1'],
  210. 'thr_1':initial_params['thr_1'],
  211. 'betx_2':np.round(maxX/2),
  212. 'bety_2':initial_params['bety_2'],
  213. 'betz_2':initial_params['betz_2'],
  214. 'thr_2':initial_params['thr_2'],
  215. 'betx_3':np.round(maxX/2),
  216. 'bety_3':initial_params['bety_3'],
  217. 'betz_3':initial_params['betz_3'],
  218. 'thr_3':initial_params['thr_3'],
  219. }
  220. else:
  221. param_dict = {'betx_1':np.round(maxX/2),
  222. 'bety_1':48,
  223. 'betz_1':11,
  224. 'thr_1':0.65,
  225. 'betx_2':np.round(maxX/2),
  226. 'bety_2':29,
  227. 'betz_2':11,
  228. 'thr_2':0.6,
  229. 'betx_3':np.round(maxX/2),
  230. 'bety_3':56,
  231. 'betz_3':2,
  232. 'thr_3':0.75,
  233. }
  234. # sliders for the spheres
  235. # first sphere, has 4 values, x,y,z and threshold
  236. col2 = widgets.VBox([widgets.IntSlider(min=0, max=maxX, step=1, value=param_dict['betx_1'], description='betx_1'),
  237. widgets.IntSlider(min=0, max=maxY, step=1, value=param_dict['bety_1'], description='bety_1'),
  238. widgets.IntSlider(min=0, max=maxZ, step=1, value=param_dict['betz_1'], description='betz_1'),
  239. widgets.FloatSlider(min=0, max=1, step=0.05, value=param_dict['thr_1'], description='thr_1'),
  240. ])
  241. # second sphere, has 4 values, x,y,z and threshold
  242. col3 = widgets.VBox([widgets.IntSlider(min=0, max=maxX, step=1, value=param_dict['betx_2'], description='betx_2'),
  243. widgets.IntSlider(min=0, max=maxY, step=1, value=param_dict['bety_2'], description='bety_2'),
  244. widgets.IntSlider(min=0, max=maxZ, step=1, value=param_dict['betz_2'], description='betz_2'),
  245. widgets.FloatSlider(min=0, max=1, step=0.05, value=param_dict['thr_2'], description='thr_2'),
  246. ])
  247. # third sphere, has 4 values, x,y,z and threshold
  248. col4 = widgets.VBox([widgets.IntSlider(min=0, max=maxX, step=1, value=param_dict['betx_3'], description='betx_3'),
  249. widgets.IntSlider(min=0, max=maxY, step=1, value=param_dict['bety_3'], description='bety_3'),
  250. widgets.IntSlider(min=0, max=maxZ, step=1, value=param_dict['betz_3'], description='betz_3'),
  251. widgets.FloatSlider(min=0, max=1, step=0.05, value=param_dict['thr_3'], description='thr_3'),
  252. ])
  253. # linkin the sliders to the function
  254. bet_app_out = widgets.interactive_output(plot_bet, {'x':col1.children[0], 'y':col1.children[1], 'z':col1.children[2],
  255. 'betx_1':col2.children[0], 'bety_1':col2.children[1], 'betz_1':col2.children[2],
  256. 'betx_2':col3.children[0], 'bety_2':col3.children[1], 'betz_2':col3.children[2],
  257. 'betx_3':col4.children[0], 'bety_3':col4.children[1], 'betz_3':col4.children[2],
  258. 'plot_mask':col1.children[3],
  259. })
  260. # button to apply the BET
  261. col5 = widgets.Button(description='Apply BET')
  262. # setting the button to call the function
  263. col5.on_click(lambda b: apply_bet_button(
  264. col2.children[0].value, col2.children[1].value, col2.children[2].value, col2.children[3].value,
  265. col3.children[0].value, col3.children[1].value, col3.children[2].value, col3.children[3].value,
  266. col4.children[0].value, col4.children[1].value, col4.children[2].value, col4.children[3].value,
  267. ))
  268. row1 = HBox([col1,col2,col3,col4])
  269. row2 = HBox([col5])
  270. bet_app_tab = VBox([row1,row2])
  271. return bet_app_tab, bet_app_out
  272. def crop_app(project_dict, sub_N):
  273. def plot_slices(x, y, z, x_lim1, y_lim1, z_lim1, x_lim2, y_lim2, z_lim2):
  274. """
  275. Plot slices from the sagittal, coronal, and axial views side by side.
  276. """
  277. # get the max in x
  278. maxX = data.shape[0]
  279. # flip x_lim1 and x_lim2
  280. x_lim1 = maxX - x_lim1
  281. x_lim2 = maxX - x_lim2
  282. fig, axes = plt.subplots(1, 3, figsize=(8, 5))
  283. # Sagittal
  284. sagittal_slice = data[x, :, :]
  285. axes[0].imshow(sagittal_slice.T, cmap='gray', origin='lower')
  286. axes[0].axis('off')
  287. # Coronal
  288. coronal_slice = data[:, y, :]
  289. axes[1].imshow(coronal_slice.T, cmap='gray', origin='lower')
  290. axes[1].axis('off')
  291. # Axial
  292. axial_slice = data[:, :, z]
  293. axes[2].imshow(axial_slice.T, cmap='gray', origin='lower')
  294. axes[2].axis('off')
  295. # make a tight layout
  296. plt.tight_layout()
  297. # make the background black
  298. fig.patch.set_facecolor('black')
  299. # Plotting red lines
  300. # plotting lines in sagital slice
  301. axes[0].axhline(y=z_lim1, color='red', lw=2)
  302. axes[0].axvline(x=y_lim1, color='red', lw=2)
  303. # plotting lines in coronal slice
  304. axes[1].axvline(x=x_lim1, color='red', lw=2)
  305. axes[1].axhline(y=z_lim1, color='red', lw=2)
  306. # plotting lines in axial slice
  307. axes[2].axvline(x=x_lim1, color='red', lw=2)
  308. axes[2].axhline(y=y_lim1, color='red', lw=2)
  309. # Plotting blue lines
  310. # plotting lines in sagital slice
  311. axes[0].axhline(y=z_lim2, color='blue', lw=2)
  312. axes[0].axvline(x=y_lim2, color='blue', lw=2)
  313. # plotting lines in coronal slice
  314. axes[1].axvline(x=x_lim2, color='blue', lw=2)
  315. axes[1].axhline(y=z_lim2, color='blue', lw=2)
  316. # plotting lines in axial slice
  317. axes[2].axvline(x=x_lim2, color='blue', lw=2)
  318. axes[2].axhline(y=y_lim2, color='blue', lw=2)
  319. plt.show()
  320. def apply_cut_button(x_lim1, y_lim1, z_lim1, x_lim2, y_lim2, z_lim2):
  321. # determine name of params_file based on mean_fct_file
  322. params_file = mean_fct_file[:-7] + '_cut_params.txt'
  323. # make sure that x_lim1 is smaller than x_lim2, if not invert them
  324. if x_lim1 > x_lim2:
  325. x_lim1, x_lim2 = x_lim2, x_lim1
  326. # make sure that y_lim1 is smaller than y_lim2, if not invert them
  327. if y_lim1 > y_lim2:
  328. y_lim1, y_lim2 = y_lim2, y_lim1
  329. # make sure that z_lim1 is smaller than z_lim2, if not invert them
  330. if z_lim1 > z_lim2:
  331. z_lim1, z_lim2 = z_lim2, z_lim1
  332. # create a dict with the cutting parameters
  333. param_dict = {'x_lim1':x_lim1, 'y_lim1':y_lim1,
  334. 'z_lim1':z_lim1, 'x_lim2':x_lim2,
  335. 'y_lim2':y_lim2, 'z_lim2':z_lim2,
  336. 'output_file':cut_mean_fct_file,
  337. }
  338. # save the cutting parameters
  339. utils.write_params_file(params_file, param_dict)
  340. print('button pressed')
  341. command = f"./remove_slices.sh {mean_fct_file} {params_file}"
  342. os.system(command)
  343. dataset = project_dict['Dataset']
  344. session = project_dict['Session']
  345. task = project_dict['Task']
  346. specie = project_dict['Specie']
  347. datafolder = project_dict['Datafolder']
  348. # working directory
  349. workingdir = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  350. mean_fct_file = workingdir + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  351. # adding session if there is one
  352. if session != '':
  353. mean_fct_file += '_ses-' + session
  354. cut_mean_fct_file = mean_fct_file + '_task-' + task + '_mean_fct.nii.gz'
  355. mean_fct_file += '_task-' + task + '_mean_fct_uncut.nii.gz'
  356. # determine min and max values for each axis
  357. img = nib.load(mean_fct_file)
  358. # Get the data from the image
  359. data = img.get_fdata()
  360. slider_style = {'description_width': 'initial', 'width': '2px'}
  361. maxX,maxY,maxZ = img.shape
  362. col1 = widgets.VBox([widgets.IntSlider(min=0, max=maxX, step=1, value=np.round(maxX/2), description='X'),
  363. widgets.IntSlider(min=0, max=maxY, step=1, value=np.round(maxY/2), description='Y'),
  364. widgets.IntSlider(min=0, max=maxZ, step=1, value=np.round(maxZ/2), description='Z')])
  365. # check if param_dict exists if yes, load it
  366. if os.path.exists(mean_fct_file[:-7] + '_cut_params.txt'):
  367. param_dict = utils.read_params_file(mean_fct_file[:-7] + '_cut_params.txt')
  368. else:
  369. param_dict = {'x_lim1':0,
  370. 'y_lim1':0,
  371. 'z_lim1':0,
  372. 'x_lim2':maxX,
  373. 'y_lim2':maxY,
  374. 'z_lim2':maxZ,
  375. }
  376. col2 = widgets.VBox([widgets.IntSlider(min=0, max=maxX-1, step=1, value=param_dict['x_lim1'], description='lim X'),
  377. widgets.IntSlider(min=0, max=maxY-1, step=1, value=param_dict['y_lim1'], description='lim Y'),
  378. widgets.IntSlider(min=0, max=maxZ-1, step=1, value=param_dict['z_lim1'], description='lim Z')])
  379. col3 = widgets.VBox([widgets.IntSlider(min=0, max=maxX-1, step=1, value=param_dict['x_lim2'], description='lim X'),
  380. widgets.IntSlider(min=0, max=maxY-1, step=1, value=param_dict['y_lim2'], description='lim Y'),
  381. widgets.IntSlider(min=0, max=maxZ-1, step=1, value=param_dict['z_lim2'], description='lim Z')])
  382. # col2 = widgets.VBox([widgets.IntSlider(min=0, max=maxX-1, step=1, value= maxX - int(param_dict['x_lim2']), description='lim X'),
  383. # widgets.IntSlider(min=0, max=maxY-1, step=1, value=param_dict['y_lim1'], description='lim Y'),
  384. # widgets.IntSlider(min=0, max=maxZ-1, step=1, value=param_dict['z_lim1'], description='lim Z')])
  385. # col3 = widgets.VBox([widgets.IntSlider(min=0, max=maxX-1, step=1, value=maxX - int(param_dict['x_lim1']), description='lim X'),
  386. # widgets.IntSlider(min=0, max=maxY-1, step=1, value=param_dict['y_lim2'], description='lim Y'),
  387. # widgets.IntSlider(min=0, max=maxZ-1, step=1, value=param_dict['z_lim2'], description='lim Z')])
  388. col4 = widgets.Button(description='Apply cut')
  389. out = widgets.interactive_output(plot_slices, {'x':col1.children[0], 'y':col1.children[1], 'z':col1.children[2],
  390. 'x_lim1':col2.children[0], 'y_lim1':col2.children[1], 'z_lim1':col2.children[2],
  391. 'x_lim2':col3.children[0], 'y_lim2':col3.children[1], 'z_lim2':col3.children[2],
  392. })
  393. # setting the button to call the function
  394. col4.on_click(lambda b: apply_cut_button(
  395. col2.children[0].value, col2.children[1].value, col2.children[2].value,
  396. col3.children[0].value, col3.children[1].value, col3.children[2].value,
  397. ))
  398. tab_crop_app = VBox([HBox([col1,col2,col3]),col4])
  399. return tab_crop_app,out
  400. def check_job_status(job):
  401. # This function will check if the job finished, failed or is still running
  402. # Right now it will return 'Finished'
  403. job_status = 'Finished'
  404. return job_status
  405. def run_process(job):
  406. '''
  407. Will select the variables from the schedule_table and the project_dict to run the process
  408. Can run:
  409. preprocess_run
  410. get_mean_fct
  411. crop_interface
  412. bet_interface
  413. mean_to_STD
  414. run_to_STD
  415. '''
  416. # get the process to run
  417. # get the other variables of the job
  418. # user = job['User']
  419. dataset = job['Dataset']
  420. session = job['Session']
  421. task = job['Task']
  422. sub_N = job['sub_N']
  423. specie = job['Specie']
  424. process = job['Process']
  425. datafolder = job['Datafolder']
  426. job_status = job['Status']
  427. combination = job['Combination']
  428. smooth = job['Smooth']
  429. atlas_type = job['Atlas_type']
  430. base_run = 1
  431. img_type = 'brain2mm'
  432. run_prepro = job['Full_prepro']
  433. variation = job['Variation']
  434. session_and_run = job['session_and_run']
  435. first_time = job['first_time']
  436. use_anatomic = job['use_anatomic']
  437. print(f"Running process: {process}")
  438. # run the adecuate process
  439. if process == 'Preprocess':
  440. print('Running preprocess')
  441. for run_N, session in zip(job['run_N'], job['Sessions']):
  442. preprocess_run(
  443. sub_N, run_N, dataset, task, specie,
  444. datafolder, session, smooth,
  445. combination, run_prepro)
  446. elif process == 'Mean fct':
  447. print('Running get_mean_fct')
  448. runs_to_use = job['run_N']
  449. sessions_to_use = job['Sessions']
  450. session_and_run = job['session_and_run']
  451. get_mean_fct(
  452. sub_N, session_and_run, base_run, dataset,
  453. task, specie, datafolder, first_time=first_time)
  454. elif process == 'Mean to atlas':
  455. print('Running mean_to_STD')
  456. mean_to_STD(
  457. sub_N, dataset, task, specie, datafolder,
  458. atlas_type, img_type, variation=variation, use_anatomic=use_anatomic)
  459. elif process == 'Runs to atlas':
  460. print('Running run_to_STD')
  461. print('Variation:', variation)
  462. for run_N, session in zip(job['run_N'], job['Sessions']):
  463. run_to_STD(
  464. sub_N, run_N, dataset, task,
  465. specie, datafolder, atlas_type,
  466. img_type, session=session)
  467. else:
  468. print('Process not found')
  469. def check_file_status(project_dict, sub_N, run_N, session, process, verbose=False,
  470. model=None, dis_method=None, rsa_method=None, radius=None, rsa_model=None,
  471. stim_N=None, stim_1_name=None, stim_2_name=None, stim_types=None,
  472. reps=None, reps_group=None):
  473. '''
  474. Check which files are available for the process
  475. returns True if the files are available, False if not
  476. '''
  477. # print the inputs
  478. # print('Sub:', sub_N, 'Run:', run_N, 'Session:', session, 'Process:', process)
  479. # get specie from the project_dict
  480. specie = project_dict['Specie']
  481. # get dataset from the project_dict
  482. dataset = project_dict['Dataset']
  483. # get task from the project_dict
  484. task = project_dict['Task']
  485. # get datafolder from the project_dict
  486. datafolder = project_dict['Datafolder']
  487. if process == 'preprocess_run': # check if the preprocess files exist
  488. # create the filename
  489. if session != '':
  490. session = int(session)
  491. filename = (datafolder + os.sep + dataset + os.sep + 'BIDS' + os.sep +
  492. specie +'-sub-' + str(sub_N).zfill(2) + os.sep +
  493. specie + '-sub-' + str(sub_N).zfill(2) +
  494. '_ses-' + str(session).zfill(2) +
  495. '_task-' + task +
  496. '_run-' + str(run_N).zfill(2) +
  497. '_bold.nii.gz')
  498. filename_json = filename[:-7] + '.json'
  499. # check if filename and filename_json exist
  500. if os.path.exists(filename):
  501. if verbose:
  502. print('BIDS file exists: ' + filename)
  503. if os.path.exists(filename_json):
  504. if verbose:
  505. print('json file exists: ' + filename_json)
  506. return True, filename
  507. else:
  508. if verbose:
  509. print('BIDS file does not exist: ' + filename)
  510. return False, filename
  511. elif process == 'get_mean_fct':
  512. outputdir = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  513. filename = specie + '-sub-' + str(sub_N).zfill(2)
  514. # adding session if there is one
  515. if session != '':
  516. session = int(session)
  517. filename += '_ses-' + f"{session:02d}"
  518. filename += '_task-' + task + '_run-' + str(run_N).zfill(2) + '_reoriented.nii.gz'
  519. # check if the file exists
  520. if os.path.exists(outputdir + os.sep + filename):
  521. if verbose:
  522. print('Reoriented file exists: ' + filename)
  523. return True, filename
  524. else:
  525. if verbose:
  526. print('Reoriented file does not exist: ' + filename)
  527. return False, filename
  528. elif process == 'mean_to_STD':
  529. # check if the mean file exists
  530. mean_fct_file = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  531. # adding session if there is one
  532. mean_fct_file += '_task-' + task + '_mean_fct_brain.nii.gz'
  533. # check if the file exists
  534. if os.path.exists(mean_fct_file):
  535. print('Mean functional file exists: ' + mean_fct_file)
  536. return True, mean_fct_file
  537. else:
  538. print('Mean functional file does not exist: ' + mean_fct_file)
  539. return False, mean_fct_file
  540. elif process == 'run_to_STD':
  541. preprocess_dir = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  542. filename = (specie + '-sub-' + str(sub_N).zfill(2) +
  543. '_ses-' + session +
  544. '_task-' + task +
  545. '_run-' + str(run_N).zfill(2)
  546. )
  547. preprocessed_file = filename + '_mc.nii.gz'
  548. # check if the file exists
  549. if os.path.exists(preprocess_dir + os.sep + preprocessed_file):
  550. if verbose:
  551. print('Motion corrected file exists: ' + preprocessed_file)
  552. return True, preprocess_dir + os.sep + preprocessed_file
  553. else:
  554. if verbose:
  555. print('Motion corrected file does not exist: ' + preprocess_dir + os.sep + preprocessed_file)
  556. return False, preprocess_dir + os.sep + preprocessed_file
  557. elif process == 'BOLD': # check if the normalized BOLD file exist
  558. # if session is int convert to str with 2 digits
  559. if isinstance(session, int):
  560. session = str(session).zfill(2)
  561. filename = (datafolder + os.sep + dataset + os.sep + 'normalized' + os.sep +
  562. specie + '-sub-' + str(sub_N).zfill(2) + os.sep +
  563. specie + '-sub-' + str(sub_N).zfill(2) +
  564. '_ses-' + session +
  565. '_task-' + task +
  566. '_run-' + str(run_N).zfill(2) +
  567. '.nii.gz')
  568. # check if filename exists
  569. if os.path.exists(filename):
  570. if verbose:
  571. print('Normalized BOLD file exists: ' + filename)
  572. return True, filename
  573. else:
  574. if verbose:
  575. print('BOLD file does not exist: ' + filename)
  576. return False, filename
  577. elif process == 'feat_folder': # check if the first level GLM file exist
  578. # model = 'basic'
  579. # "P:\userdata\raulh87\data\EmoB\results\GLM\basic\D-sub-01\ses-01_task-EmoB_run-01.feat\stats\tstat1.nii.gz"
  580. # if session is int convert to str with 2 digits
  581. # check that all variables are available, if not indicate which is missing
  582. if model is None: # model is required
  583. print('Model is missing')
  584. return False, 'error in model'
  585. filename = (datafolder + os.sep + dataset + os.sep + 'results' + os.sep + 'GLM' + os.sep +
  586. model + os.sep + f"{specie}-sub-{sub_N:02d}" + os.sep + f"ses-{session}_task-{task}_run-{run_N:02d}.feat")
  587. # check if filename exist
  588. if os.path.exists(filename):
  589. if verbose:
  590. print('feat folder exists: ' + filename)
  591. return True, filename
  592. else:
  593. if verbose:
  594. print('feat folder does not exist: ' + filename)
  595. return False, filename
  596. # NOTE: the 'beta_map'/'beta_maps' branches below probe the *legacy* FEAT
  597. # layout (.feat/stats/pe*). They predate step 0.5 and have no callers today
  598. # (both check_file_status callers pass process='GLM'). If you revive them,
  599. # route through rsa_utils.resolve_beta_map instead -- these paths report
  600. # "missing" for any run whose .feat has been deleted after step 0.5.
  601. # They are not fixed in place because rsa_utils imports this module, so
  602. # importing it back here would be circular.
  603. elif process == 'beta_map': # check if the beta map exist
  604. # build the filename for the beta map
  605. # make sure that all variables are available, if not indicate which is missing
  606. list_vars = [model, stim_N]
  607. list_vars_name = ['model', 'stim_N']
  608. for var, var_name in zip(list_vars, list_vars_name):
  609. if var is None:
  610. print(f'{var_name} is missing')
  611. return False, f'error in {var_name}'
  612. # build the filename for the beta map
  613. filename = (datafolder + os.sep + dataset + os.sep + 'results' + os.sep + 'GLM' + os.sep +
  614. model + os.sep + f"{specie}-sub-{sub_N:02d}" + os.sep +
  615. f"ses-{session}_task-{task}_run-{run_N:02d}.feat" + os.sep + "stats" + os.sep + f"pe{stim_N*2 - 1}.nii.gz")
  616. # check if filename exist
  617. if os.path.exists(filename):
  618. if verbose:
  619. print('beta map exists: ' + filename)
  620. return True, filename
  621. else:
  622. if verbose:
  623. print('beta map does not exist: ' + filename)
  624. return False, filename
  625. elif process == 'beta_maps': # check if the beta map exist
  626. # build the filename for the beta map
  627. # make sure that all variables are available, if not indicate which is missing
  628. list_vars = [model, stim_types]
  629. list_vars_name = ['model', 'stim_types']
  630. for var, var_name in zip(list_vars, list_vars_name):
  631. if var is None:
  632. print(f'{var_name} is missing')
  633. return False, f'error in {var_name}'
  634. # initialize a list to store missing files
  635. files_missing, files_found = [], []
  636. for stim_Nx,_ in enumerate(stim_types):
  637. # starts at 1 and jumps of 2
  638. stim_N = stim_Nx + 1
  639. filename = (datafolder + os.sep + dataset + os.sep + 'results' + os.sep + 'GLM' + os.sep +
  640. model + os.sep + f"{specie}-sub-{sub_N:02d}" + os.sep +
  641. f"ses-{session}_task-{task}_run-{run_N:02d}.feat" + os.sep + "stats" + os.sep + f"pe{stim_N*2 - 1}.nii.gz")
  642. # add to files_found if exist
  643. if os.path.exists(filename):
  644. files_found.append(filename)
  645. else:
  646. files_missing.append(filename)
  647. if len(files_missing) == 0:
  648. if verbose:
  649. print('All beta maps exist')
  650. # return true and a list of all filenames
  651. return True, files_found
  652. else:
  653. if verbose:
  654. print('Some beta maps are missing')
  655. print('Missing files:', files_missing)
  656. return False, files_missing
  657. elif process == 'pairwise_similarity_maps':
  658. # build the filename for the pairwise similarity map
  659. # make sure that all variables are available, if not indicate which is missing
  660. list_vars = [model, dis_method, radius, stim_types]
  661. list_vars_name = ['model', 'dis_method', 'radius', 'stim_types']
  662. for var, var_name in zip(list_vars, list_vars_name):
  663. if var is None:
  664. print(f'{var_name} is missing')
  665. return False, f'error in {var_name}'
  666. # initialize a list to store missing files
  667. files_missing, files_found = [], []
  668. # check if "P:\userdata\raulh87\data\EmoB\results\RSA\basic-block\D-sub-01\ses-01_task-EmoB_run-01\r-3_correlation_A-1_A-2.nii.gz"
  669. for stim_1_N, stim_1_name in enumerate(stim_types):
  670. for stim_2_N, stim_2_name in enumerate(stim_types):
  671. if stim_2_N > stim_1_N: # only check for upper triangle
  672. filename = (datafolder + os.sep + dataset + os.sep + 'results' + os.sep + 'RSA' + os.sep +
  673. model + os.sep + f"{specie}-sub-{sub_N:02d}" + os.sep +
  674. f"ses-{session}_task-{task}_run-{run_N:02d}" + os.sep +
  675. f"r-{radius}_{dis_method}_{stim_1_name}_{stim_2_name}.nii.gz")
  676. # add to files_found if exist
  677. if os.path.exists(filename):
  678. if verbose:
  679. print('Exists: ' + filename + 'adding...')
  680. files_found.append(filename)
  681. else:
  682. if verbose:
  683. print('Missing: ' + filename + 'adding to missing...')
  684. files_missing.append(filename)
  685. if len(files_missing) == 0:
  686. if verbose:
  687. print('All pairwise similarity maps exist')
  688. # return true and a list of all filenames
  689. return True, files_found
  690. else:
  691. if verbose:
  692. print('Some pairwise similarity maps are missing')
  693. print('Missing files:', files_missing)
  694. return False, files_missing
  695. elif process == 'pairwise_similarity_map':
  696. # build the filename for the pairwise similarity map
  697. # make sure that all variables are available, if not indicate which is missing
  698. list_vars = [model, dis_method, radius, stim_1_name, stim_2_name]
  699. list_vars_name = ['model', 'dis_method', 'radius', 'stim_1_name', 'stim_2_name']
  700. for var, var_name in zip(list_vars, list_vars_name):
  701. if var is None:
  702. print(f'{var_name} is missing')
  703. return False, f'error in {var_name}'
  704. # build the filename for the pairwise similarity map
  705. filename = (datafolder + os.sep + dataset + os.sep + 'results' + os.sep + 'RSA' + os.sep +
  706. model + os.sep + f"{specie}-sub-{sub_N:02d}" + os.sep +
  707. f"ses-{session}_task-{task}_run-{run_N:02d}" + os.sep +
  708. f"r-{radius}_{dis_method}_{stim_1_name}_{stim_2_name}.nii.gz")
  709. # check if filename exist
  710. if os.path.exists(filename):
  711. if verbose:
  712. print('pairwise similarity map exists: ' + filename)
  713. return True, filename
  714. else:
  715. if verbose:
  716. print('pairwise similarity map does not exist: ' + filename)
  717. return False, filename
  718. elif process == 'model_similarity_map':
  719. # build the filename for the model similarity map
  720. # make sure that all variables are available, if not indicate which is missing
  721. list_vars = [model, dis_method, rsa_method, radius, rsa_model]
  722. list_vars_name = ['model', 'dis_method', 'rsa_method', 'radius', 'rsa_model'
  723. ]
  724. for var, var_name in zip(list_vars, list_vars_name):
  725. if var is None:
  726. print(f'{var_name} is missing')
  727. return False, f'error in {var_name}'
  728. filename = (datafolder + os.sep + dataset + os.sep + 'results' + os.sep + 'RSA' + os.sep +
  729. model + os.sep + rsa_model + os.sep + f"{specie}-sub-{sub_N:02d}" + os.sep + f"ses-{session}_task-{task}_run-{run_N:02d}" + os.sep +
  730. f"r-{radius}_{dis_method}_{rsa_method}.nii.gz")
  731. # check if filename exist
  732. if os.path.exists(filename):
  733. if verbose:
  734. print('model similarity map exists: ' + filename)
  735. return True, filename
  736. else:
  737. if verbose:
  738. print('model similarity map does not exist: ' + filename)
  739. return False, filename
  740. # write check for beta map
  741. elif process == 'mean_model_similarity_map':
  742. # build the filename for the mean model similarity map
  743. # make sure that all variables are available, if not indicate which is missing
  744. list_vars = [model, dis_method, rsa_method, radius, rsa_model]
  745. list_vars_name = ['model', 'dis_method', 'rsa_method', 'radius', 'rsa_model'
  746. ]
  747. for var, var_name in zip(list_vars, list_vars_name):
  748. if var is None:
  749. print(f'{var_name} is missing')
  750. return False, f'error in {var_name}'
  751. filename = (datafolder + os.sep + dataset + os.sep + 'results' + os.sep + 'RSA' + os.sep +
  752. model + os.sep + rsa_model + os.sep + 'mean' + os.sep +
  753. f"r-{radius}_{dis_method}_{rsa_method}_mean.nii.gz")
  754. # check if filename exist
  755. if os.path.exists(filename):
  756. if verbose:
  757. print('mean model similarity map exists: ' + filename)
  758. return True, filename
  759. else:
  760. if verbose:
  761. print('mean model similarity map does not exist: ' + filename)
  762. return False, filename
  763. elif process == 'model_similarity_maps_rnd':
  764. # checks if the permutations have been done:
  765. # true, files_found. If all files exist, false if one or more files are missing
  766. # false, missing_files. If one or more files are missing, list of missing files
  767. # make sure that all variables are available, if not indicate which is missing
  768. # reps - number of by participant repetitions
  769. list_vars = [model, dis_method, rsa_method, radius, rsa_model, reps]
  770. list_vars_name = ['model', 'dis_method', 'rsa_method', 'radius', 'rsa_model'
  771. 'reps']
  772. # check that all variables are available, if not indicate which is missing
  773. for var, var_name in zip(list_vars, list_vars_name):
  774. if var is None:
  775. print(f'{var_name} is missing')
  776. return False, f'error in {var_name}'
  777. # initialize a list to store missing files
  778. files_missing, files_found, file_num_missing = [], [], []
  779. # "P:\userdata\raulh87\data\EmoB\results\RSA_rnd\basic\emotion_valence\D-sub-01\ses-01_task-EmoB_run-01\r-3_pearson_kendall_0000.nii.gz"
  780. for rep in range(reps):
  781. filename = (datafolder + os.sep + dataset + os.sep + 'results' + os.sep + 'RSA_rnd' + os.sep +
  782. model + os.sep + rsa_model + os.sep + f"{specie}-sub-{sub_N:02d}" + os.sep + f"ses-{session}_task-{task}_run-{run_N:02d}" + os.sep +
  783. f"r-{radius}_{dis_method}_{rsa_method}_{str(rep).zfill(4)}.nii.gz")
  784. # add to files_found if exist
  785. if os.path.exists(filename):
  786. files_found.append(filename)
  787. else:
  788. files_missing.append(filename)
  789. file_num_missing.append(rep)
  790. if len(files_missing) == 0:
  791. if verbose:
  792. print('All model similarity rnd maps exist')
  793. # return true and a list of all filenames
  794. return True, files_found
  795. else:
  796. if verbose:
  797. print('Some model similarity rnd maps are missing')
  798. print('Missing files:', files_missing)
  799. return False, file_num_missing
  800. elif process == 'mean_model_similarity_maps_rnd':
  801. # checks if the permutations have been done:
  802. # true, files_found. If all files exist, false if one or more files are missing
  803. # false, missing_files. If one or more files are missing, list of missing files
  804. # make sure that all variables are available, if not indicate which is missing
  805. # reps - number of by participant repetitions
  806. list_vars = [model, dis_method, rsa_method, radius, rsa_model, reps, reps_group]
  807. list_vars_name = ['model', 'dis_method', 'rsa_method', 'radius', 'rsa_model',
  808. 'reps_group']
  809. # check that all variables are available, if not indicate which is missing
  810. for var, var_name in zip(list_vars, list_vars_name):
  811. if var is None:
  812. print(f'{var_name} is missing')
  813. return False, f'error in {var_name}'
  814. # initialize a list to store missing files
  815. files_missing, files_found, file_num_missing = [], [], []
  816. # "P:\userdata\raulh87\data\EmoB\results\RSA_rnd\basic\emotion_valence\r-r-3_pearson_kendall_mean_07181.nii.gz"
  817. for g_rep in range(reps_group):
  818. filename = (datafolder + os.sep + dataset + os.sep +
  819. 'results' + os.sep + 'RSA_rnd' + os.sep +
  820. model + os.sep + rsa_model + os.sep +
  821. 'r-' + str(radius) + '_' + dis_method + '_' + rsa_method + '_mean_' + str(g_rep).zfill(len(str(reps_group))) + '.nii.gz')
  822. # add to files_found if exist
  823. if os.path.exists(filename):
  824. files_found.append(filename)
  825. else:
  826. files_missing.append(filename)
  827. file_num_missing.append(rep)
  828. if len(files_missing) == 0:
  829. if verbose:
  830. print('All model similarity rnd maps exist')
  831. # return true and a list of all filenames
  832. return True, files_found
  833. else:
  834. if verbose:
  835. print('Some model similarity rnd maps are missing')
  836. print('Missing files:', files_missing)
  837. return False, file_num_missing
  838. elif process == 'get_vectors':
  839. # "P:\userdata\raulh87\data\EmoB\BIDS\sub-01\sub-01_ses-01_task-EmoB_run-01_events.csv"
  840. # if session is int convert to str with 2 digits
  841. if isinstance(session, int):
  842. session = str(session).zfill(2)
  843. # check if the events file exists
  844. filename = (datafolder + os.sep + dataset + os.sep + 'BIDS' + os.sep +
  845. specie + '-sub-' + str(sub_N).zfill(2) + os.sep +
  846. specie + '-sub-' + str(sub_N).zfill(2) +
  847. '_ses-' + session +
  848. '_task-' + task +
  849. '_run-' + str(run_N).zfill(2) + '_events.csv')
  850. # check if the file exists
  851. if os.path.exists(filename):
  852. if verbose:
  853. print('File exists: ' + filename)
  854. return True, filename
  855. else:
  856. if verbose:
  857. print('File does not exist: ' + filename)
  858. return False, filename
  859. else:
  860. # print process (process) not found
  861. print('Process not found: ' + process)
  862. return False, 'error in process'
  863. def preprocess_run(sub_N, run_N, dataset, task, specie, datafolder, session, smooth=0, combination=['-x','z','-y'], run_prepro=True):
  864. """
  865. Preprocesses a single run of a single subject.
  866. Reorients file
  867. """
  868. ## determine input file and output directory ##
  869. # input directory in BIDS format
  870. input_folder = datafolder + os.sep + dataset + os.sep + 'BIDS' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  871. filename = input_folder + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + '_ses-' + session + '_task-' + task + '_run-' + str(run_N).zfill(2) + '_bold.nii.gz'
  872. filename_json = input_folder + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + '_ses-' + session + '_task-' + task + '_run-' + str(run_N).zfill(2) + '_bold.json'
  873. # print filename
  874. print('Input file: ' + filename)
  875. # get TR and number of volumes
  876. TR,volumes = utils.extract_params(filename)
  877. # create output directory, where the fsl output will be saved (preprocessed data)
  878. outputdir = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  879. fsl_outputdir = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + '_ses-' + session + '_task-' + task + '_run-' + str(run_N).zfill(2)
  880. slice_timming_path = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + os.sep + 'slice_timming_' + specie + '-sub-' + str(sub_N).zfill(2) + '_ses-' + session + '_task-' + task + '_run-' + str(run_N).zfill(2) + '.txt'
  881. # check that slice_timming_path folder exist
  882. if not os.path.exists(os.path.dirname(slice_timming_path)):
  883. os.makedirs(os.path.dirname(slice_timming_path))
  884. # get slice timing parameters
  885. slice_timming = utils.get_slice_timing(filename_json, slice_timming_path)
  886. # make sure the file was created
  887. if not os.path.exists(slice_timming_path):
  888. print('Slice timing file was not created: ' + slice_timming_path)
  889. return
  890. # check if slice_timming is not empty
  891. if slice_timming is None:
  892. print('No slice timing parameters found')
  893. print('File: ' + slice_timming_path + ' empty')
  894. return
  895. ## Filling out the design.fsf file ##
  896. # create list of labels to fill in the design.fsf file
  897. label_list = ['Outputdir', 'TR', 'Volumes', 'BET', 'Smooth', 'Input', 'SliceTimming']
  898. # create dictionary to fill in the design.fsf file
  899. to_fill_dict = dict()
  900. for label in label_list:
  901. to_fill_dict[label] = dict()
  902. if label == 'Outputdir':
  903. to_fill_dict[label]['string_to_find'] = 'set fmri(outputdir)'
  904. to_fill_dict[label]['string_to_replace'] = ('set fmri(outputdir) "' + fsl_outputdir + '"')
  905. elif label == 'TR':
  906. to_fill_dict[label]['string_to_find'] = 'set fmri(tr)'
  907. to_fill_dict[label]['string_to_replace'] = ('set fmri(tr) ' + str(TR))
  908. elif label == 'Volumes':
  909. to_fill_dict[label]['string_to_find'] = 'set fmri(npts)'
  910. to_fill_dict[label]['string_to_replace'] = ('set fmri(npts) ' + str(volumes))
  911. elif label == 'BET':
  912. to_fill_dict[label]['string_to_find'] = 'set fmri(bet_yn)'
  913. if specie == 'H':
  914. to_fill_dict[label]['string_to_replace'] = ('set fmri(bet_yn) 1')
  915. elif specie == 'D':
  916. to_fill_dict[label]['string_to_replace'] = ('set fmri(bet_yn) 0')
  917. elif label == 'Smooth':
  918. to_fill_dict[label]['string_to_find'] = 'set fmri(smooth)'
  919. to_fill_dict[label]['string_to_replace'] = ('set fmri(smooth) ' + str(smooth))
  920. elif label == 'Input':
  921. to_fill_dict[label]['string_to_find'] = 'set feat_files(1)'
  922. to_fill_dict[label]['string_to_replace'] = ('set feat_files(1) "' + filename + '"')
  923. elif label == 'SliceTimming':
  924. to_fill_dict[label]['string_to_find'] = 'set fmri(st_file)'
  925. to_fill_dict[label]['string_to_replace'] = ('set fmri(st_file) "' + slice_timming_path + '"')
  926. # fill in the design.fsf file
  927. design_path = os.path.join(os.getcwd(), 'FSL_designs' + os.sep + 'preprocess_slice_timming_from_JSON.fsf')
  928. # design_path = os.path.join(os.getcwd(), 'FSL_designs' + os.sep + 'preprocess_no-slice-timing.fsf')
  929. print('Design path: ' + design_path)
  930. design_modified_path = os.path.join(os.getcwd(), 'FSL_designs' + os.sep + 'preprocess_modified.fsf')
  931. if run_prepro:
  932. utils.fill_fsf(to_fill_dict, design_path, design_modified_path)
  933. # check if previous feat preprocessing directory exists, if so, delete it
  934. if os.path.exists(fsl_outputdir + '.feat'):
  935. shutil.rmtree(fsl_outputdir + '.feat')
  936. # run feat
  937. command = 'feat ' + design_modified_path
  938. # check if system is windows, if so, do not execute command
  939. print(command)
  940. if os.name == 'nt':
  941. print("The system is windows, command not executed.")
  942. elif os.name == 'posix':
  943. os.system(command)
  944. else:
  945. os.system(command)
  946. else:
  947. print('Preprocessing not run, skipping this step')
  948. ## reorient run ##
  949. base_filename =(
  950. specie + '-sub-' + str(sub_N).zfill(2) +
  951. '_ses-' + session +
  952. '_task-' + task +
  953. '_run-' + str(run_N).zfill(2))
  954. # non-oriented file
  955. non_oriented_file = base_filename + '_not-oriented.nii.gz'
  956. # oriented file
  957. reoriented_file = base_filename + '_reoriented.nii.gz'
  958. preprocessed_file = fsl_outputdir + '.feat' + os.sep + 'filtered_func_data.nii.gz'
  959. # check if the system is windows
  960. if os.name == 'nt':
  961. print('A copy should have been created, but this is Windows')
  962. print(non_oriented_file + ' a copy of this file here:')
  963. print('FSL output directory: ' + fsl_outputdir)
  964. else:
  965. #copy preprocessed_file to non_oriented_file
  966. shutil.copyfile(preprocessed_file, outputdir + os.sep + non_oriented_file)
  967. print(non_oriented_file + ' created')
  968. print('FSL output directory: ' + fsl_outputdir)
  969. # check if system is windows, if so, do not execute command
  970. if os.name == 'nt': # Windows
  971. print("system is windows, command not executed.")
  972. print("orientation to use: ",combination)
  973. print("non-oriented file: " + outputdir + os.sep + non_oriented_file)
  974. print("oriented file: " + outputdir + os.sep + reoriented_file)
  975. utils.reorient_file(outputdir + os.sep + non_oriented_file, outputdir + os.sep + reoriented_file, combination)
  976. def get_mean_fct(sub_N, session_and_run, base_run, dataset, task, specie, datafolder, first_time=True):
  977. """
  978. Calculates the mean functional image for a subject and a task.
  979. The mean functional image is calculated by averaging the mean images of each run.
  980. The mean image of each run is calculated by averaging all volumes of the run.
  981. The mean image of each run is calculated by averaging all volumes of the run.
  982. The motion is corrected for each run using the first volume of the first run as reference.
  983. The motion parameters are saved in a .par file.
  984. The mean image of each run
  985. """
  986. # Check if the system is windows
  987. if os.name == 'nt':
  988. print('The system is Windows, this is a test, no actual system or FSL commands will be run')
  989. # output directory where the fsl output will be saved (preprocessed data)
  990. outputdir = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  991. # movement directory
  992. movementdir = datafolder + os.sep + dataset + os.sep + 'movement'
  993. # create movement directory if it does not exist
  994. if not os.path.exists(movementdir):
  995. os.makedirs(movementdir)
  996. # get initial run from session_and_run[0] = ['ses-01_run-1', 'ses-02_run-2']
  997. initial_run = int(session_and_run[0].split('_')[1].split('-')[1])
  998. initial_session = session_and_run[0].split('_')[0].split('-')[1]
  999. ## obtain volume to be used as base to correct all others ##
  1000. filename = (specie + '-sub-' + str(sub_N).zfill(2) +
  1001. '_ses-' + initial_session +
  1002. '_task-' + task +
  1003. '_run-' + str(initial_run).zfill(2) +
  1004. '_reoriented.nii.gz')
  1005. if first_time: # get the volume
  1006. # get the first volume of the first run to use as base volume
  1007. command = f"fslroi {outputdir + os.sep + filename} {outputdir + os.sep + 'base_vol.nii.gz'} 0 1"
  1008. # print commmand
  1009. print(command)
  1010. # if the system is not windows, run the command
  1011. if os.name != 'nt':
  1012. os.system(command)
  1013. else:
  1014. # will use average created before
  1015. print('Using existing base volume')
  1016. # check if base_vol exists
  1017. if not os.path.exists(outputdir + os.sep + 'base_vol.nii.gz'):
  1018. print('base_vol.nii.gz does not exist, run the code with first_time = True')
  1019. print('path: ' + outputdir + os.sep + 'base_vol.nii.gz')
  1020. raise ValueError('base_vol.nii.gz does not exist, run the code with first_time = True')
  1021. ## ----- ##
  1022. # This string will be used to generate the mean image
  1023. mean_images = ''
  1024. ## calculate motion for each run and generate par file ##
  1025. for n, file_ending in enumerate(session_and_run):
  1026. print('processing ' + file_ending)
  1027. session = file_ending.split('_')[0].split('-')[1]
  1028. run_N = int(file_ending.split('_')[1].split('-')[1])
  1029. print('processing ' + str(n+1) + ' of ' + str(len(session_and_run)) + ' runs')
  1030. filename = (specie + '-sub-' + str(sub_N).zfill(2) +
  1031. '_ses-' + session +
  1032. '_task-' + task +
  1033. '_run-' + str(run_N).zfill(2))
  1034. # '_reoriented.nii.gz')
  1035. # average the reoriented file to create a mean image
  1036. print('calculating mean image...')
  1037. command = f"fslmaths {outputdir + os.sep + filename + '_reoriented.nii.gz'} -Tmean {outputdir + os.sep + filename + '_mean_unaligned.nii.gz'}"
  1038. # print commmand
  1039. print(command)
  1040. # if the system is not windows, run the command
  1041. if os.name != 'nt':
  1042. os.system(command)
  1043. # command to calculate transformation matrix from mean image to base volume
  1044. command = f"flirt -in {outputdir + os.sep + filename + '_mean_unaligned.nii.gz'} -ref {outputdir + os.sep + 'base_vol.nii.gz'} -out {outputdir + os.sep + filename + '_mean_aligned_tmp.nii.gz'} -omat {outputdir + os.sep + filename + '2base_vol.mat'}"
  1045. # print commmand
  1046. print(command)
  1047. if os.name != 'nt':
  1048. os.system(command)
  1049. # apply the transformation matrix to the 4D file
  1050. command = f"applyxfm4D {outputdir + os.sep + filename + '_reoriented.nii.gz'} {outputdir + os.sep + 'base_vol.nii.gz'} {outputdir + os.sep + filename + '_mc.nii.gz'} {outputdir + os.sep + filename + '2base_vol.mat'} -singlematrix"
  1051. # print commmand
  1052. print(command)
  1053. # if the system is not windows, run the command
  1054. if os.name != 'nt':
  1055. os.system(command)
  1056. # apply the transformation matrix to the file
  1057. print('calculating mean image...')
  1058. command = f"fslmaths {outputdir + os.sep + filename + '_mc.nii.gz'} -Tmean {outputdir + os.sep + filename + '_mean.nii.gz'}"
  1059. # print commmand
  1060. print(command)
  1061. # if the system is not windows, run the command
  1062. if os.name != 'nt':
  1063. os.system(command)
  1064. # if the system is not windows, run the command
  1065. # print file saved
  1066. print('aligned file saved as ' + filename + '_mc.nii.gz')
  1067. # add filename to mean_images
  1068. mean_images += outputdir + os.sep + filename + '_mean.nii.gz' + ' '
  1069. if first_time: # if yes, calculate mean image
  1070. mean_fct_file = (outputdir + os.sep +
  1071. specie + '-sub-' + str(sub_N).zfill(2) +
  1072. '_task-' + task + '_mean_fct_uncut.nii.gz'
  1073. )
  1074. # append mean images to a single 4D image
  1075. command = f"fslmerge -t {mean_fct_file} {mean_images}"
  1076. # print commmand
  1077. print(command)
  1078. # if the system is not windows, run the command
  1079. if os.name != 'nt':
  1080. os.system(command)
  1081. print('mean fct file saved as ' + mean_fct_file)
  1082. # calculate mean image
  1083. command = f"fslmaths {mean_fct_file} -Tmean {mean_fct_file}"
  1084. # print commmand
  1085. print(command)
  1086. # if the system is not windows, run the command
  1087. if os.name != 'nt':
  1088. os.system(command)
  1089. print('done')
  1090. def mean_to_STD(sub_N, dataset, task, specie, datafolder, atlas_type, img_type='brain2mm', variation='STD0', use_anatomic=False):
  1091. """
  1092. This function will take the mean functional image of a subject and transform it to the space of the atlas.
  1093. sub_N: subject number
  1094. dataset: dataset name
  1095. task: task name
  1096. specie: specie name
  1097. datafolder: path to the data folder
  1098. atlas_type: type of atlas to use
  1099. img_type: type of image to use, default is 'brain2mm'
  1100. """
  1101. # working directory
  1102. workingdir = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  1103. mean_fct_file = workingdir + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  1104. cut_mean_fct_file = mean_fct_file + '_task-' + task + '_mean_fct.nii.gz'
  1105. mean_fct_file += '_task-' + task + '_mean_fct.nii.gz'
  1106. masked_mean_fct_file = mean_fct_file[:-7] + '_brain.nii.gz'
  1107. mean_fct_file_STD = mean_fct_file[:-7] + '_STD.nii.gz'
  1108. mean_fct2STD_mat = mean_fct_file[:-7] + '2STD.mat'
  1109. #T1w_brain_file = datafolder + os.sep + dataset + os.sep + 'BIDS' + os.sep + 'sub-' + str(sub_N).zfill(2) + os.sep + 'sub-' + str(sub_N).zfill(2) + '_T1w_brain.nii.gz'
  1110. T1w_brain_file = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + '_T1w_brain.nii.gz'
  1111. T1w_brain_file_unoriented = datafolder + os.sep + dataset + os.sep + 'BIDS' + os.sep + 'sub-' + str(sub_N).zfill(2) + os.sep + 'sub-' + str(sub_N).zfill(2) + '_T1w_brain.nii.gz'
  1112. T1w_brain_STD_file = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + '_T1w_brain_STD.nii.gz'
  1113. T1w_brain2STD_mat = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + os.sep + specie + '-sub-' + str(sub_N).zfill(2)+ '_T1w_brain2STD.mat'
  1114. mean_fct_T1w_file = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + os.sep + specie + '-sub-' + str(sub_N).zfill(2)+ '_mean_fct_T1w.nii.gz'
  1115. mean_fct2T1w_mat = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + os.sep + specie + '-sub-' + str(sub_N).zfill(2)+ '_mean_fct2T1w.mat'
  1116. if specie == 'D':
  1117. specieS = 'Dog'
  1118. elif specie == 'H':
  1119. specieS = 'Hum'
  1120. # generate path to atlas. The atlas is in the same folder as the script
  1121. atlas_file = os.getcwd() + os.sep + "Atlas" + os.sep + specieS + os.sep + atlas_type + os.sep + img_type + ".nii.gz"
  1122. # check which variation to use
  1123. # A - mean
  1124. # B - anatomic
  1125. # C - STD
  1126. if use_anatomic:
  1127. # mean_to_anatomic(sub_N, dataset, task, specie, datafolder) # A -> B
  1128. # anatomic_to_STD(sub_N, dataset, task, specie, datafolder, atlas_type, img_type=img_type) # B -> C
  1129. # apply fslreorient2std to the T1w_brain file
  1130. print('Reorienting T1w_brain file to standard orientation')
  1131. command = f"fslreorient2std {T1w_brain_file_unoriented} {T1w_brain_file}"
  1132. print(command)
  1133. #if system is not windows, run the command
  1134. if os.name != 'nt':
  1135. os.system(command)
  1136. print('Transforming masked mean functional image to T1w_brain A -> B')
  1137. # masked_mean_fct to T1w_brain A -> B
  1138. command = f"flirt -in {masked_mean_fct_file} -ref {T1w_brain_file} -out {mean_fct_T1w_file} -omat {mean_fct2T1w_mat} -bins 256 -cost corratio -searchrx -30 30 -searchry -30 30 -searchrz -30 30 -dof 7 -interp trilinear"
  1139. # if the system is windows, don't run the command, just write it down
  1140. print(command)
  1141. if os.name == 'nt': # Windows
  1142. print("System is Windows, command not executed")
  1143. else:
  1144. os.system(command)
  1145. # T1w_brain to atlas B -> C T1w_brain_STD_file
  1146. print('Transforming T1w_brain to atlas space B -> C')
  1147. command = f"flirt -in {T1w_brain_file} -ref {atlas_file} -out {T1w_brain_STD_file} -omat {T1w_brain2STD_mat} -bins 256 -cost corratio -searchrx -45 45 -searchry -45 45 -searchrz -45 45 -dof 12 -interp trilinear"
  1148. # flirt -in ${dataFolder}/data/${sub}/masks/${brain_nMeanfct}.nii.gz -ref ${atlasFile} -out ${dataFolder}/data/${sub}/masks/brainSTD.nii.gz -omat ${dataFolder}/data/${sub}/masks/brain2STD.mat -bins 256 -cost corratio -searchrx -45 45 -searchry -45 45 -searchrz -45 45 -dof 12 -interp trilinear
  1149. print(command)
  1150. if os.name != 'nt': # Windows
  1151. os.system(command)
  1152. print('Calculating transformation matrix from mean functional image to atlas A->B, B->C = A->C')
  1153. # adding the matrices... #A->B + B->C = A->C
  1154. command = f"convert_xfm -omat {mean_fct2STD_mat} -concat {T1w_brain2STD_mat} {mean_fct2T1w_mat}"
  1155. print(command)
  1156. if os.name == 'nt': # Windows
  1157. print("System is Windows, command not executed")
  1158. else:
  1159. os.system(command)
  1160. # apply the transformation matrix to the mean functional image
  1161. print('Applying transformation matrix to masked mean functional image')
  1162. command = f"flirt -in {masked_mean_fct_file} -ref {atlas_file} -out {mean_fct_file_STD} -applyxfm -init {mean_fct2STD_mat} -interp trilinear"
  1163. else: # use mean functional image directly
  1164. print('Transforming masked mean functional image directly to atlas space')
  1165. print('variation: ' + variation)
  1166. if variation == 'STD0':
  1167. command = f"flirt -in {masked_mean_fct_file} -ref {atlas_file} -out {mean_fct_file_STD} -omat {mean_fct2STD_mat} -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 -interp trilinear"
  1168. elif variation == 'STD1':
  1169. command = f"flirt -in {masked_mean_fct_file} -ref {atlas_file} -out {mean_fct_file_STD} -omat {mean_fct2STD_mat} -bins 256 -cost corratio -searchrx -30 30 -searchry -30 30 -searchrz -30 30 -dof 12 -interp trilinear"
  1170. else: #error, variation not found
  1171. print('Variation not found')
  1172. #generate error message
  1173. return
  1174. # if the system is windows, don't run the command, just write it down
  1175. print(command)
  1176. if os.name == 'nt': # Windows
  1177. print("System is Windows, command not executed")
  1178. else:
  1179. os.system(command)
  1180. def mean_to_anatomic(sub_N, dataset, task, specie, datafolder):
  1181. '''
  1182. input:
  1183. \BIDS\sub-01_T1w_brain.nii.gz: anatomic file in BIDS format, skull removed, reoriented and labeled
  1184. output:
  1185. '''
  1186. workingdir = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  1187. T1w_brain_file = datafolder + os.sep + dataset + os.sep + 'BIDS' + os.sep + 'sub-' + str(sub_N).zfill(2) + '_T1w_brain.nii.gz'
  1188. mean_fct_T1w_file = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + '_mean_fct_T1w.nii.gz'
  1189. mean_fct2T1w_mat = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + '_mean_fct2T1w.mat'
  1190. mean_fct_file = workingdir + os.sep + specie + '-sub-' + str(sub_N).zfill(2) # like: D-sub-01_task-EmoB_run-01_mean_fct.nii.gz
  1191. mean_fct_file += '_task-' + task + '_mean_fct.nii.gz'
  1192. masked_mean_fct_file = mean_fct_file[:-7] + '_brain.nii.gz'
  1193. # mean_fct_file_STD = mean_fct_file[:-7] + '_STD.nii.gz'
  1194. # mean_fct2STD_mat = mean_fct_file[:-7] + '2STD.mat'
  1195. if specie == 'D':
  1196. specieS = 'Dog'
  1197. elif specie == 'H':
  1198. specieS = 'Hum'
  1199. # masked_mean_fct to T1w_brain
  1200. command = f"flirt -in {masked_mean_fct_file} -ref {T1w_brain_file} -out {mean_fct_T1w_file} -omat {mean_fct2T1w_mat} -bins 256 -cost corratio -searchrx -30 30 -searchry -30 30 -searchrz -30 30 -dof 7 -interp trilinear"
  1201. # if the system is windows, don't run the command, just write it down
  1202. print(command)
  1203. if os.name == 'nt': # Windows
  1204. print("System is Windows, command not executed")
  1205. else:
  1206. os.system(command)
  1207. def anatomic_to_STD(sub_N, dataset, task, specie, datafolder, atlas_type, img_type='brain2mm'):
  1208. '''
  1209. input:
  1210. \BIDS\sub-01_T1w_brain.nii.gz: anatomic file in BIDS format, skull removed, reoriented and labeled
  1211. output:
  1212. '''
  1213. workingdir = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  1214. T1w_brain_file = datafolder + os.sep + dataset + os.sep + 'BIDS' + os.sep + 'sub-' + str(sub_N).zfill(2) + '_T1w_brain.nii.gz'
  1215. T1w_brain_STD_file = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + '_T1w_brain_STD.nii.gz'
  1216. T1w_brain2STD_mat = datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep + specie + '-sub-' + str(sub_N).zfill(2) + '_T1w_brain2STD.mat'
  1217. if specie == 'D':
  1218. specieS = 'Dog'
  1219. elif specie == 'H':
  1220. specieS = 'Hum'
  1221. # generate path to atlas. The atlas is in the same folder as the script
  1222. atlas_file = os.getcwd() + os.sep + "Atlas" + os.sep + specieS + os.sep + atlas_type + os.sep + img_type + ".nii.gz"
  1223. # T1w_brain to atlas
  1224. command = f"flirt -in {T1w_brain_file} -ref {atlas_file} -out {T1w_brain_STD_file} -omat {T1w_brain2STD_mat} -bins 256 -cost corratio -searchrx -30 30 -searchry -30 30 -searchrz -30 30 -dof 7 -interp trilinear"
  1225. # if the system is windows, don't run the command, just write it down
  1226. print(command)
  1227. if os.name == 'nt': # Windows
  1228. print("System is Windows, command not executed")
  1229. else:
  1230. os.system(command)
  1231. def run_to_STD(sub_N, run_N, dataset, task, specie, datafolder, atlas_type, img_type='brain2mm', session=''):
  1232. """
  1233. This function will take a semi-processed run of a participant
  1234. cut it, apply BET and transform it to the space of the atlas.
  1235. sub_N: subject number
  1236. run_N: run number
  1237. dataset: dataset name
  1238. task: task name
  1239. specie: specie name
  1240. datafolder: path to the data folder
  1241. atlas_type: type of atlas to use
  1242. img_type: type of image to use, default is 'brain2mm'
  1243. session: session number (in case there is one)
  1244. """
  1245. # if the system is windows
  1246. if os.name == 'nt':
  1247. print('The system is Windows, FSL functions ans bash scripts will not be executed')
  1248. # working directories
  1249. std_dir = datafolder + os.sep + dataset + os.sep + 'normalized' + os.sep + specie + '-sub-' + str(sub_N).zfill(2)
  1250. preprocess_dir = (datafolder + os.sep + dataset + os.sep + 'preprocessing' + os.sep +
  1251. specie + '-sub-' + str(sub_N).zfill(2)
  1252. )
  1253. # cutting parameters file
  1254. params_file = (preprocess_dir + os.sep +
  1255. specie + '-sub-' + str(sub_N).zfill(2) +
  1256. '_task-' + task + '_mean_fct_uncut_cut_params.txt'
  1257. )
  1258. filename = (specie + '-sub-' + str(sub_N).zfill(2) +
  1259. '_ses-' + session +
  1260. '_task-' + task +
  1261. '_run-' + str(run_N).zfill(2))
  1262. # name for reoriented and motion corrected file
  1263. preprocessed_file = filename + '_mc.nii.gz'
  1264. cut_file = filename + '_reoriented_mc_cut.nii.gz'
  1265. # updating output params_file
  1266. params_dict = utils.read_params_file(params_file)
  1267. params_dict['output_file'] = preprocess_dir + os.sep + cut_file
  1268. params_file_current = preprocess_dir + os.sep + filename + '_cut_params.txt'
  1269. # params_file_current = params_file[:-30] + '_run-' + str(run_N).zfill(2) + '_cut_params.txt'
  1270. # add mask file to parameters
  1271. params_dict['mask_file'] = (preprocess_dir + os.sep +
  1272. specie + '-sub-' + str(sub_N).zfill(2) +
  1273. '_task-' + task + '_mean_fct_mask.nii.gz')
  1274. # save the cutting parameters
  1275. utils.write_params_file(params_file_current, params_dict)
  1276. # cut preprocessed file and apply BET
  1277. command = f"./remove_slices.sh {preprocess_dir + os.sep + preprocessed_file} {params_file_current}"
  1278. print(command)
  1279. if os.name != 'nt': # Windows
  1280. os.system(command)
  1281. # apply transformation to STD
  1282. mean_fct2STD_mat = (
  1283. preprocess_dir + os.sep +
  1284. specie + '-sub-' + str(sub_N).zfill(2) +
  1285. '_task-' + task + '_mean_fct2STD.mat'
  1286. )
  1287. # determine folder for the atlas
  1288. if specie == 'D':
  1289. specieS = 'Dog'
  1290. elif specie == 'H':
  1291. specieS = 'Hum'
  1292. atlas_file = os.getcwd() + os.sep + "Atlas" + os.sep + specieS + os.sep + atlas_type + os.sep + img_type + ".nii.gz"
  1293. # if normalized directory does not exist, create it
  1294. if not os.path.exists(std_dir):
  1295. os.makedirs(std_dir)
  1296. print('Directory ' + std_dir + ' created')
  1297. else:
  1298. print('Directory ' + std_dir + ' already exists')
  1299. command = f"flirt -in {preprocess_dir + os.sep + cut_file} -ref {atlas_file} -applyxfm -init {mean_fct2STD_mat} -out {std_dir + os.sep + filename + '.nii.gz'} -interp trilinear"
  1300. print(command)
  1301. if os.name != 'nt': # Windows
  1302. os.system(command)
  1303. print('done')
  1304. from pathlib import Path
  1305. import numpy as np
  1306. from scipy.signal import detrend as _sp_detrend
  1307. def fwd(par_file, radius=50.0, threshold=0.5, detrend_type="linear-demean", output_file=None, add_movement_params=True):
  1308. """
  1309. Compute framewise displacement (FD) and return a binary mask of frames above threshold.
  1310. If written to disk, the file includes the 6 motion parameters followed by one
  1311. additional censoring column per volume that exceeded the FD threshold.
  1312. Parameters
  1313. ----------
  1314. par_file : str or Path
  1315. Path to FSL .par file with 6 motion parameters (rotations in radians first).
  1316. radius : float, optional
  1317. Radius in mm used to convert rotations to displacement (default is 50).
  1318. threshold : float, optional
  1319. Threshold in mm for FD (default is 0.5).
  1320. detrend_type : str, optional
  1321. Detrending method: 'linear-demean', 'linear-nodemean', or 'none' (default is 'linear-demean').
  1322. output_file : str or Path, optional
  1323. If provided, saves a .txt file with the 6 motion parameters followed by one
  1324. censoring column for each excluded volume.
  1325. add_movement_params : bool, optional
  1326. If True, includes motion parameters in the output file (default is True).
  1327. Returns
  1328. -------
  1329. numpy.ndarray
  1330. Array of 1s and 0s where 1 = frame above or equal to threshold, 0 = below.
  1331. """
  1332. par_file = Path(par_file)
  1333. motion = np.loadtxt(par_file, ndmin=2, dtype=float)
  1334. if motion.shape[1] != 6:
  1335. raise ValueError(f"Expected 6 motion columns, got {motion.shape[1]}")
  1336. motion_detrended = _detrend_columns(motion, detrend_type)
  1337. motion_detrended[:, :3] *= radius # convert radians to mm
  1338. d_motion = np.vstack([np.zeros((1, 6)), np.diff(motion_detrended, axis=0)])
  1339. fd = np.sum(np.abs(d_motion), axis=1)
  1340. mask = (fd >= threshold).astype(np.int8)
  1341. if output_file:
  1342. if add_movement_params:
  1343. excluded_idx = np.flatnonzero(mask)
  1344. censor_columns = np.zeros((motion.shape[0], excluded_idx.size), dtype=float)
  1345. if excluded_idx.size > 0:
  1346. censor_columns[excluded_idx, np.arange(excluded_idx.size)] = 1.0
  1347. output_data = np.hstack([motion, censor_columns])
  1348. np.savetxt(output_file, output_data, fmt="%.6f", delimiter="\t")
  1349. print(f"Motion parameters and {excluded_idx.size} censoring columns saved to {output_file}")
  1350. else:
  1351. if mask.size == 0:
  1352. np.savetxt(output_file, np.empty((0, 0)), fmt="%d", delimiter="\t")
  1353. else:
  1354. np.savetxt(output_file, mask, fmt="%d", delimiter="\t")
  1355. print(f"FD mask saved to {output_file}")
  1356. return mask
  1357. def _detrend_columns(arr, kind="linear-demean"):
  1358. if kind == "none":
  1359. return arr.copy()
  1360. out = arr.copy()
  1361. demean = kind == "linear-demean"
  1362. if kind in {"linear-demean", "linear-nodemean"}:
  1363. for i in range(arr.shape[1]):
  1364. col = arr[:, i]
  1365. mu = col.mean() if demean else 0.0
  1366. out[:, i] = _sp_detrend(col) + mu
  1367. else:
  1368. raise NotImplementedError("Only 'linear-demean', 'linear-nodemean', and 'none' are supported.")
  1369. return out

preprocess_functions.py at commit 55ed729, no license · at the source

Overview

Authors: Raúl Hernández-Pérez1,2,3, Luis Concha4, Attila Andics2,3, Rodolfo Bernal-Gamboa5, Laura V. Cuaya1,2
  1. Social, Cognitive and Affective Neuroscience Unit, Department of Cognition, Emotion, and Methods in Psychology, Faculty of Psychology, University of Vienna, Liebiggasse 5, 1010 Vienna, Austria
  2. Neuroethology of Communication Lab, Department of Ethology, Institute of Biology, Eötvös Loránd University, Pázmány Péter Sétány 1/C, 1117 Budapest, Hungary
  3. ELTE NAP Canine Brain Research Group, Pázmány Péter Sétány 1/C, 1117 Budapest, Hungary
  4. Department of Behavioral and Cognitive Neurobiology, Institute of Neurobiology, National Autonomous University of Mexico, Campus Juriquilla, Boulevard Juriquilla 3001, Querétaro 76230, México
  5. Faculty of Psychology, National Autonomous University of Mexico, Circuito Ciudad Universitaria, Mexico City 04510, México
Journal: iScience, volume 29, issue 8, article 116900
Dates: received 25 August 2025; accepted 7 July 2026; published online 10 August 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1016/j.isci.2026.116900 · PMID 42666977 · PMCID PMC13523845 · OpenAlex W7202098964
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: fMRI (modality), human (organism), other (organism), cognitive (subfield)
Methods: Connectivity, Statistics, Machine learning, Preprocessing, fMRI & imaging
Keywords: dogs, social perception, human facial expressions, affective neuroscience, functional magnetic resonance imaging, fMRI
Topic: Human-Animal Interaction Studies (Genetics, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Funding: Austrian Science Fund (10.55776, ESP602); European Research Council; ERC; Horizon 2020 (950159); Hungarian Academy of Sciences; MTA-ELTE; Lendület; Neuroethology of Communication Research Group (LP2017-13/2017); National Brain Programme 3.0 (NAP2022-I-3/2022); R.H.-P; L.V.C; Consejo Nacional de Humanidades, Ciencias y Tecnologías (409258, 407590, 181508, 1782); Universidad Nacional Autónoma de México (IB201712, IG200117, IN204720)
Citations: not cited yet (Europe PMC); 95 references in the paper

Abstract

Dogs can distinguish human facial expressions, particularly happiness, yet brain processes remain unclear. Using fMRI, we conducted two experiments in awake pet dogs. In Experiment 1 (n = 8), happy faces elicited a stronger response than neutral faces in a right temporal cluster extending to the caudate nucleus, including the rostral Sylvian gyrus. In Experiment 2 (n = 12), dogs viewed faces expressing happiness, anger, fear, or sadness. Using the Experiment 1 cluster as a region of interest, a machine-learning classifier distinguished happiness from each negative facial expression, but not between negative pairs, showing differential BOLD responsiveness to happy faces. Whole-brain representational similarity analyses revealed activity patterns differentiating angry vs. fearful faces (right mid ectosylvian and left splenial gyri) and sad vs. fearful faces (right rostral suprasylvian gyrus) but not angry vs. sad faces. This provides direct evidence that dog brains can distinguish between two negative facial expressions.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 5 matches between paragraphs and lines of code.

rhernandez00/dog_brain_toolkit

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 55ed729dab520cc0786c3c97e8a26d3f232abf5f, 22 September 2026
Languages: Python (66), MATLAB (53), Jupyter (12), Shell (4), JavaScript (1)
Size: 4,135 files, 136 scripts
Software Heritage: not archived
Found in: the references
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (32 files), pandas (30 files), NiBabel (28 files), Tools for NIfTI and ANALYZE image (MATLAB) (18 files), PyTorch (16 files), FSL (10 files), Matplotlib (7 files), SciPy (6 files), Nilearn (5 files), Plotly (3 files), Image Processing Toolbox (1 file), Signal Processing Toolbox (1 file), Statistics and Machine Learning Toolbox (1 file), scikit-learn (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
137 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 136 scripts, each with its path and the digest of its content;
  • 5 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data and code availability

The data associated with this manuscript have been deposited at Zenodo, and the custom preprocessing and analysis code used in this study is available through the Dog Brain Toolkit on GitHub. Both resources are publicly available, and their DOIs are listed in the key resources table. Any additional information required to reanalyze the data reported in this work is available from the lead contact upon request.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 3, 28 September 2026

  • Authors: added Raúl Hernández-Pérez (0000-0003-3971-6236); Laura V. Cuaya (0000-0002-1073-7300); removed Raúl Hernández-Pérez; Laura V. Cuaya

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 6 keywords, 13 funders, 90 references.

Cite

This paper

Hernández-Pérez, R., Concha, L., Andics, A., Bernal-Gamboa, R., & Cuaya, L. V. (2026). Dog brain representations of human facial expressions: Encoding happiness and differentiating specific negative expressions. iScience, 29(8), 116900. https://doi.org/10.1016/j.isci.2026.116900

BibTeX

@article{hernandezperez2026dog,
author = {Hernández-Pérez, Raúl and Concha, Luis and Andics, Attila and Bernal-Gamboa, Rodolfo and Cuaya, Laura V.},
title = {{Dog brain representations of human facial expressions: Encoding happiness and differentiating specific negative expressions}},
journal = {iScience},
year = {2026},
month = aug,
volume = {29},
number = {8},
pages = {116900},
publisher = {Elsevier},
issn = {2589-0042},
doi = {10.1016/j.isci.2026.116900},
url = {https://doi.org/10.1016/j.isci.2026.116900},
pmid = {42666977},
pmcid = {PMC13523845}
}

RIS

TY - JOUR
AU - Hernández-Pérez, Raúl
AU - Concha, Luis
AU - Andics, Attila
AU - Bernal-Gamboa, Rodolfo
AU - Cuaya, Laura V.
TI - Dog brain representations of human facial expressions: Encoding happiness and differentiating specific negative expressions
T2 - iScience
J2 - iScience
PY - 2026
DA - 2026/08/10
VL - 29
IS - 8
SP - 116900
SN - 2589-0042
PB - Elsevier
DO - 10.1016/j.isci.2026.116900
UR - https://doi.org/10.1016/j.isci.2026.116900
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.isci.2026.116900",
"type": "article-journal",
"title": "Dog brain representations of human facial expressions: Encoding happiness and differentiating specific negative expressions",
"container-title": "iScience",
"author": [
{
"family": "Hernández-Pérez",
"given": "Raúl"
},
{
"family": "Concha",
"given": "Luis"
},
{
"family": "Andics",
"given": "Attila"
},
{
"family": "Bernal-Gamboa",
"given": "Rodolfo"
},
{
"family": "Cuaya",
"given": "Laura V."
}
],
"container-title-short": "iScience",
"volume": "29",
"issue": "8",
"page": "116900",
"DOI": "10.1016/j.isci.2026.116900",
"PMID": "42666977",
"PMCID": "PMC13523845",
"ISSN": "2589-0042",
"publisher": "Elsevier",
"URL": "https://doi.org/10.1016/j.isci.2026.116900",
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
10
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s41467-026-73668-y [code]
Convergent and divergent brain-cognition development in early adolescence.
Journal: Nature communications
In common: Tools for NIfTI and ANALYZE image (MATLAB), FSL, Nilearn, 10 other tools, fMRI, 1 reference
[2] doi:10.21203/rs.3.rs-9326213/v1 [code]
Multi-task fMRI outperforms resting-state fMRI for revealing task-invariant organization of the human brain
Journal: Research Square (preprint)
In common: FSL, Nilearn, Plotly, 8 other tools, fMRI, cognitive, 2 references
[3] doi:10.64898/2026.03.09.710558 [code]
Multi-task fMRI outperforms resting-state fMRI for revealing task-invariant organization of the human brain
Journal: bioRxiv (preprint)
In common: FSL, Nilearn, Plotly, 8 other tools, fMRI, cognitive, 2 references
[4] doi:10.1016/j.xcrm.2026.102943 [code]
Parent-of-origin effects in Alzheimer's liability dissociate neurocognitive and cardiovascular traits in at-risk individuals.
Journal: Cell reports. Medicine
In common: Tools for NIfTI and ANALYZE image (MATLAB), FSL, Nilearn, 10 other tools
[5] doi:10.1002/hbm.70483 [code]
Untamed: Unconstrained Tensor Decomposition and Graph Node Embedding for Cortical Parcellation.
Journal: Human brain mapping
In common: Tools for NIfTI and ANALYZE image (MATLAB), FSL, Nilearn, 9 other tools, fMRI
[6] doi:10.1038/s41586-026-10631-3 [code]
A prognostic human brain network for diffuse midline glioma.
Journal: Nature
In common: Tools for NIfTI and ANALYZE image (MATLAB), FSL, Nilearn, 9 other tools, 1 reference
[7] doi:10.1016/j.neuron.2026.04.011 [code]
Precision fMRI reveals densely interdigitated network patches with conserved motifs in the lateral prefrontal cortex.
Journal: Neuron
In common: Tools for NIfTI and ANALYZE image (MATLAB), FSL, Nilearn, 8 other tools, fMRI, 1 reference
[8] doi:10.3389/fnagi.2026.1742371 [code]
Cross-sectional and longitudinal functional network alterations associated with subthreshold depressive symptoms in healthy older adults.
Journal: Frontiers in aging neuroscience
In common: Tools for NIfTI and ANALYZE image (MATLAB), FSL, Nilearn, 8 other tools, fMRI, 1 reference
[9] doi:10.1038/s41592-026-03154-2 [code]
Simultaneous single-cell calcium imaging of neuronal population activity and brain-wide BOLD fMRI.
Journal: Nature methods
In common: FSL, Nilearn, Image Processing Toolbox, 8 other tools, fMRI, 2 references
[10] doi:10.1038/s41467-026-76452-0 [code]
Music evokes shared neural representations of imagined narratives across sensory modalities.
Journal: Nature communications
In common: Tools for NIfTI and ANALYZE image (MATLAB), FSL, Nilearn, 9 other tools, cognitive

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.