OSCR

Quantitative MRI Uncovers Subtle Cortical Damage in Myelin Oligodendrocyte Glycoprotein Antibody-Associated Disease.

Code ↔ Paper

1 match 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 1 match
  1. [1] § Methods › MRI Acquisition and Processing ↔ myelin_map_funcs.py, lines 648–788 · score 0.54 · myelin_map, pipeline, calibrated, eye, brain, masks

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 · 788 lines · 30 KB · no license · 1 match

  1. from __future__ import division
  2. import numpy as np
  3. import nibabel as nb
  4. import scipy.stats as stats
  5. from scipy.ndimage.morphology import binary_erosion as be
  6. from statsmodels import robust
  7. from nibabel import processing as nbproc
  8. import matplotlib.pyplot as plt
  9. import seaborn as sns
  10. from nipype.interfaces import ants, dcm2nii
  11. import fnmatch
  12. import glob
  13. import os
  14. def dcm_convert(scan_dict, output_dir):
  15. subj = os.path.split(output_dir)[-1]
  16. dir_files = os.listdir(output_dir)
  17. if fnmatch.filter(dir_files, 't1.nii.gz') and fnmatch.filter(dir_files, 't2.nii.gz'):
  18. print('DCM to nifti conversion already run for {}. Not re-running.'.format(subj))
  19. #Get output from previous run, return in dictionary.
  20. dcm_out = glob.glob(os.path.join(output_dir, 't?.nii.gz'))
  21. t1_subj_nii = dcm_out[0]
  22. t2_subj_nii = dcm_out[1]
  23. else:
  24. scan_list = list(scan_dict.keys())
  25. anat_convert_output = []
  26. for scan in scan_list:
  27. anat_convert = dcm2nii.Dcm2niix()
  28. anat_convert.inputs.source_names = scan_dict[scan]
  29. anat_convert.inputs.out_filename = scan
  30. anat_convert.inputs.output_dir = output_dir
  31. anat_convert.inputs.compress = 'y'
  32. results = anat_convert.run()
  33. anat_convert_output.append(results.outputs.get()['converted_files'])
  34. t1_subj_nii, t2_subj_nii = anat_convert_output
  35. return(t1_subj_nii, t2_subj_nii)
  36. ###PLOTTING###
  37. #Plot histograms of data
  38. def plot_mask_dist(t1_fn, t2_fn, eye_mask_fn, temp_bone_mask_fn, stat = None):
  39. t1 = nb.load(t1_fn).get_data().ravel()
  40. t2 = nb.load(t2_fn).get_data().ravel()
  41. eye_mask = nb.load(eye_mask_fn).get_data().ravel()
  42. temp_mask = nb.load(temp_bone_mask_fn).get_data().ravel()
  43. if stat == 'mean':
  44. t1_eye_stat = np.mean(t1[eye_mask])
  45. t1_temp_stat = np.mean(t1[temp_mask])
  46. t2_eye_stat = np.mean(t2[eye_mask])
  47. t2_temp_stat = np.mean(t2[temp_mask])
  48. elif stat == 'median':
  49. t1_eye_stat = np.median(t1[eye_mask])
  50. t1_temp_stat = np.median(t1[temp_mask])
  51. t2_eye_stat = np.median(t2[eye_mask])
  52. t2_temp_stat = np.median(t2[temp_mask])
  53. elif stat == 'mode' or stat == None:
  54. t1_eye_stat = stats.mode(t1[eye_mask][t1[eye_mask] > 0])[0][0]
  55. t1_temp_stat = stats.mode(t1[temp_mask][t1[temp_mask] > 0])[0][0]
  56. t2_eye_stat = stats.mode(t2[eye_mask][t2[eye_mask] > 0])[0][0]
  57. t2_temp_stat = stats.mode(t2[temp_mask][t2[temp_mask] > 0])[0][0]
  58. fig = plt.figure(figsize = [15, 10]);
  59. plt.subplot(2,1,1);
  60. plt.title('Eye ' + stat)
  61. sns.distplot(t1[eye_mask], label = 'T1');
  62. ymin, ymax = fig.gca().axes.get_ybound()
  63. plt.vlines(x = t1_eye_stat, ymin = ymin, ymax = ymax)
  64. sns.distplot(t2[eye_mask], label = 'T2');
  65. plt.vlines(x = t2_eye_stat, ymin = ymin, ymax = ymax)
  66. plt.legend()
  67. #plt.show()
  68. fig = plt.figure(figsize = [15, 10]);
  69. plt.subplot(2,1,2);
  70. plt.title('Temporal Bone ' + stat)
  71. sns.distplot(t1[temp_mask], label = 'T1');
  72. ymin, ymax = fig.gca().axes.get_ybound()
  73. plt.vlines(x = t1_temp_stat, ymin = ymin, ymax = ymax)
  74. sns.distplot(t2[temp_mask], label = 'T2');
  75. plt.vlines(x = t2_temp_stat, ymin = ymin, ymax = ymax)
  76. plt.legend()
  77. #plt.show()
  78. plt.savefig('modes.png')
  79. print('T1 eye: {}\nT2 eye: {}\nT1 temp: {}\nT2 temp: {}'.format(t1_eye_stat, t2_eye_stat, t1_temp_stat, t2_temp_stat))
  80. #Plot image overlays to assess warp quality
  81. def plot_ants_warp(fixed, moving, nslices, output_name = None):
  82. """
  83. Plots nslices axial images of the fixed and moving images from the ants transform
  84. inputs:
  85. anat - Fixed image (image the moving was warped to)
  86. moving - Moving image (Transformed image)
  87. nslices - Number of slices to plot
  88. output_name - filename to save png image to (optional)
  89. """
  90. fixed_data = nb.load(fixed).get_data()
  91. moving_data = nb.load(moving).get_data()
  92. view_slices = np.linspace(100, fixed_data.shape[1] - 1, num = nslices).astype(int)
  93. fig = plt.figure(figsize = [50, 25])
  94. for n, view_slice in enumerate(view_slices):
  95. plt.subplot(1, nslices, n + 1)
  96. plt.imshow(fixed_data[:, :, view_slice], cmap = 'Greys_r')
  97. plt.imshow(moving_data[:, :, view_slice], cmap = 'Reds', alpha = 0.25)
  98. plt.axis('off')
  99. plt.text(1,1, 'z = ' + str(view_slice), color = [1,0,0], bbox=dict(facecolor=[0,0,0]), fontsize = 20)
  100. plt.tight_layout()
  101. plt.show()
  102. if output_name != None:
  103. fig.savefig(fname = output_name + '.png')
  104. ###SPACIAL TRANSORMS###
  105. #Warp MNI to subj
  106. def ants_reg(fixed = None, moving = None, prefix = None, fixed_mask = None, moving_mask = None, output_dir = None):
  107. '''
  108. Uses ANTs to warp the moving image to the space of the fixed.
  109. Both fixed and moving images can have masks.
  110. If warping subjects with lesion damage, it is recommended to warp from MNI to subj space
  111. (fixed = subj, moving = mni template, fixed_mask = lesion mask) then
  112. use the inverse transform to warp from subj to MNI.
  113. '''
  114. subj = os.path.split(output_dir)[-1]
  115. if 'output_warped_image.nii.gz' in os.listdir(output_dir):
  116. print("Ants registration already run. Not re-running for subj {}. You're welcome.".format(subj))
  117. #Get output from previous run, return in dictionary.
  118. reg_trans = glob.glob(output_dir + '/reg_trans_*')
  119. reg_trans.append(glob.glob(output_dir + '/output_warped_image*')[0])
  120. reg_trans.sort()
  121. reg_labels = ['warped_image', 'trans_mat', 'composite_transform', 'inverse_composite_transform']
  122. reg_out_files = {k: reg_trans[n] for n, k in enumerate(reg_labels)}
  123. print(subj, reg_out_files)
  124. else:
  125. reg = ants.Registration()
  126. reg.inputs.fixed_image = fixed
  127. reg.inputs.moving_image = moving
  128. if fixed_mask != None:
  129. reg.inputs.fixed_mask = fixed_mask
  130. if moving_mask != None:
  131. reg.inputs.moving_mask = fixed_mask
  132. if prefix != None:
  133. reg.inputs.output_transform_prefix = prefix
  134. else:
  135. reg.inputs.output_transform_prefix = os.path.join(output_dir, 'reg_trans_')
  136. reg.inputs.transforms = ['Rigid', 'Affine', 'SyN']
  137. reg.inputs.transform_parameters = [(0.1,),(0.1,),(0.1, 3.0, 0.0)] #Size of movement for registration (Optimal values are 0.1-0.25.)
  138. reg.inputs.number_of_iterations =[[1000, 500, 250, 100],[1000, 500, 250, 100],[100, 70, 50, 20]]
  139. reg.inputs.dimension = 3
  140. # '''
  141. # Align the moving_image and fixed_image before registration using the geometric
  142. # center of the images (=0), the image intensities (=1),or the origin of the images (=2)
  143. # '''
  144. reg.inputs.initial_moving_transform_com = 0
  145. reg.inputs.write_composite_transform = True
  146. reg.inputs.collapse_output_transforms = False
  147. reg.inputs.initialize_transforms_per_stage = False
  148. reg.inputs.metric = ['MI', 'MI', 'CC']
  149. reg.inputs.radius_or_number_of_bins = [32, 32, 4]
  150. reg.inputs.sampling_strategy = ['Regular','Regular','None']
  151. reg.inputs.sampling_percentage = [0.25, 0.25, 1]
  152. reg.inputs.convergence_threshold = [1e-06]
  153. reg.inputs.convergence_window_size = [10]
  154. reg.inputs.smoothing_sigmas = [[3, 2, 1, 0]] * 3
  155. reg.inputs.sigma_units = ['vox'] * 3
  156. reg.inputs.shrink_factors = [[8, 4, 2, 1]] * 3
  157. reg.inputs.use_estimate_learning_rate_once = [True, True, True]
  158. reg.inputs.use_histogram_matching = True
  159. if output_dir != None:
  160. reg.inputs.output_warped_image = os.path.join(output_dir, 'output_warped_image.nii.gz')
  161. else:
  162. reg.inputs.output_warped_image = './output_warped_image.nii.gz'
  163. reg.inputs.num_threads = 6
  164. reg.inputs.metric_weight = [1.0] * 3
  165. reg.inputs.winsorize_lower_quantile = 0.005
  166. reg.inputs.winsorize_upper_quantile = 0.995
  167. reg.inputs.verbose = True
  168. reg_results = reg.run()
  169. reg_out_files = reg_results.outputs.get()
  170. return(reg_out_files)
  171. #Transform eye + temporal mask + brain mask from MNI to subj
  172. def mask_transform(mask_list, ref, transmat, output_dir):
  173. '''
  174. Transforms masks from MNI to subj space
  175. masks = list of eye + temporal mask images
  176. transmat = mapping from MNI > subj space
  177. outputs:
  178. '''
  179. subj = os.path.split(output_dir)[-1]
  180. dir_files = os.listdir(output_dir)
  181. if fnmatch.filter(dir_files, '*mask_subj*'):
  182. print('Mask transforms already run. Not re-running for subj {}'.format(subj))
  183. subj_trans_masks = glob.glob(os.path.join(output_dir, '*mask_subj.nii.gz'))
  184. subj_trans_masks.sort() #Sure, this COULD have been a dict, but eh.
  185. print(subj, 'subj masks', subj_trans_masks)
  186. else:
  187. subj_trans_masks = []
  188. for mask in mask_list:
  189. image_file = os.path.split(mask)[1]
  190. image_name = image_file.split('.')[0]
  191. print(image_name)
  192. mni2subj = ants.ApplyTransforms()
  193. mni2subj.inputs.input_image = mask
  194. mni2subj.inputs.reference_image = ref
  195. mni2subj.inputs.transforms = transmat
  196. mni2subj.inputs.interpolation = 'NearestNeighbor'
  197. mni2subj.inputs.output_image = os.path.join(output_dir, image_name + '_subj.nii.gz')
  198. mni2subj_results = mni2subj.run()
  199. output_image = mni2subj_results.outputs.get()['output_image']
  200. subj_trans_masks.append(output_image)
  201. subj_trans_masks.sort() #Ditto
  202. return(subj_trans_masks)
  203. #Register T2 to T1
  204. def ants_rigid(fixed = None, moving = None, prefix = None, fixed_mask = None, moving_mask = None, output_dir = None):
  205. subj = os.path.split(output_dir)[-1]
  206. dir_files = os.listdir(output_dir)
  207. if fnmatch.filter(dir_files, 'output_rigid*'):
  208. print("Ants rigid transformation already run. Not re-running for subj {}".format(subj))
  209. rigid_out_files = {'warped_image': os.path.join(output_dir, 'output_rigid_image.nii.gz')}
  210. print('Rigid out', rigid_out_files)
  211. else:
  212. print("Performing rigid registration of T1 and T2 images for subj {}".format(subj))
  213. rigid = ants.Registration()
  214. rigid.inputs.fixed_image = fixed
  215. rigid.inputs.moving_image = moving
  216. if fixed_mask != None:
  217. rigid.inputs.fixed_mask = fixed_mask
  218. if moving_mask != None:
  219. rigid.inputs.moving_mask = fixed_mask
  220. if prefix != None:
  221. rigid.inputs.output_transform_prefix = prefix
  222. else:
  223. rigid.inputs.output_transform_prefix = os.path.join(output_dir, 'rigid_trans_')
  224. rigid.inputs.transforms = ['Affine']
  225. rigid.inputs.transform_parameters = [(0.1,)] #Size of movement for registration (Optimal values are 0.1-0.25.)
  226. rigid.inputs.number_of_iterations = [[1000, 500, 250, 100]]
  227. rigid.inputs.dimension = 3
  228. #
  229. # Align the moving_image and fixed_image before registration using the geometric
  230. # center of the images (=0), the image intensities (=1),or the origin of the images (=2)
  231. #
  232. rigid.inputs.initial_moving_transform_com = 0
  233. rigid.inputs.write_composite_transform = True
  234. rigid.inputs.collapse_output_transforms = False
  235. rigid.inputs.initialize_transforms_per_stage = False
  236. rigid.inputs.metric = ['MI']
  237. rigid.inputs.radius_or_number_of_bins = [32]
  238. rigid.inputs.sampling_strategy = ['Regular']
  239. rigid.inputs.sampling_percentage = [0.25]
  240. rigid.inputs.convergence_threshold = [1e-06]
  241. rigid.inputs.convergence_window_size = [10]
  242. rigid.inputs.smoothing_sigmas = [[3, 2, 1, 0]]
  243. rigid.inputs.sigma_units = ['vox']
  244. rigid.inputs.shrink_factors = [[8, 4, 2, 1]]
  245. rigid.inputs.use_estimate_learning_rate_once = [True]
  246. rigid.inputs.use_histogram_matching = [True]
  247. if output_dir != None:
  248. rigid.inputs.output_warped_image = os.path.join(output_dir, 'output_rigid_image.nii.gz')
  249. else:
  250. rigid.inputs.output_warped_image = './output_rigid_image.nii.gz'
  251. rigid.inputs.num_threads = 6
  252. rigid.inputs.metric_weight = [1.0]
  253. rigid.inputs.winsorize_lower_quantile = 0.005
  254. rigid.inputs.winsorize_upper_quantile = 0.995
  255. rigid.inputs.verbose = True
  256. rigid.inputs.interpolation = 'NearestNeighbor' #Started with NN, gave good output.
  257. rigid_results = rigid.run()
  258. rigid_out_files = rigid_results.outputs.get()
  259. print('Rigid out', rigid_out_files)
  260. return(rigid_out_files)
  261. #Myelin map to MNI
  262. def subj2mni(moving = None, ref = None, transmat = None, output_dir = None):
  263. subj = os.path.split(output_dir)[-1]
  264. print('Warping {} to MNI space'.format(subj))
  265. subj2mni = ants.ApplyTransforms()
  266. subj2mni.inputs.dimension = 3
  267. subj2mni.inputs.input_image = moving
  268. subj2mni.inputs.reference_image = ref
  269. subj2mni.inputs.transforms = transmat
  270. if output_dir != None:
  271. subj2mni.inputs.output_image = os.path.join(output_dir, 'myelin_map_mni.nii.gz')
  272. else:
  273. subj2mni.inputs.output_image = ('myelin_map_mni.nii.gz')
  274. subj2mni.inputs.interpolation = 'NearestNeighbor'
  275. subj2mni_results = subj2mni.run()
  276. subj2mni_output = subj2mni_results.outputs.get()
  277. subj2mni_im = subj2mni_output['output_image']
  278. return(subj2mni_output, subj2mni_im)
  279. ###INTENSITY MANIPULATION###
  280. #Bias correction
  281. def bias_corr(images, output_dir):
  282. '''
  283. Uses N4 bias correction to remove intensity inhomogeneities
  284. Input:
  285. images = List of images (w/ paths) to be corrected
  286. '''
  287. subj = os.path.split(output_dir)[-1]
  288. dir_files = os.listdir(output_dir)
  289. if fnmatch.filter(dir_files, '*bias*'):
  290. print("Bias correction already run. Not re-running for subj {}".format(subj))
  291. bias_output = glob.glob(os.path.join(output_dir, '*_bias_corr.nii.gz'))
  292. bias_output.sort(key = len) #Assumes T2 image has been rigidly transformed to t1
  293. else:
  294. print("Running bias correction for subj {}".format(subj))
  295. bias_output = []
  296. for n, image in enumerate(images):
  297. image_file = os.path.split(image)[1]
  298. image_name = image_file.split('.')[0]
  299. print(image, image_name)
  300. n4 = ants.N4BiasFieldCorrection()
  301. n4.inputs.dimension = 3
  302. n4.inputs.input_image = image
  303. n4.inputs.bspline_fitting_distance = 300
  304. n4.inputs.shrink_factor = 2
  305. n4.inputs.n_iterations = [50,50,30,20]
  306. n4.inputs.save_bias = True
  307. n4.inputs.bias_image = os.path.join(output_dir, image_name + '_bias_field.nii.gz')
  308. n4.inputs.output_image = os.path.join(output_dir, image_name + '_bias_corr.nii.gz')
  309. n4.inputs.num_threads = 6
  310. n4_results = n4.run()
  311. output_image = n4_results.outputs.get()['output_image']
  312. bias_output.append(output_image)
  313. return(bias_output)
  314. def image_smooth(image_fn, fwhm = None, output_dir = None):
  315. subj = os.path.split(output_dir)[-1]
  316. print('\nSmoothing {} with kernel size: {}'.format(subj, fwhm))
  317. im_name = os.path.split(image_fn)[-1].split('.')[0]
  318. im_hdr = nb.load(image_fn)
  319. smoothed = nbproc.smooth_image(img = im_hdr, fwhm = fwhm)
  320. output_fn = os.path.join(output_dir, im_name + '_smoothed_' + str(fwhm) + '_mm.nii.gz')
  321. nb.Nifti1Image(smoothed.get_data(), affine = im_hdr.affine, header = im_hdr.header).to_filename(output_fn)
  322. return(output_fn)
  323. #Calibration stage
  324. def image_calibration(t1_subj_bias_corr = None,
  325. t2_subj_bias_corr = None,
  326. t1_mni_bias_corr = None,
  327. t2_mni_bias_corr = None,
  328. eye_subj_mask = None,
  329. temp_bone_subj_mask = None,
  330. brain_subj_mask = None,
  331. eye_mni_mask = None,
  332. temp_bone_mni_mask = None,
  333. brain_mni_mask = None, output_dir = None):
  334. subj = os.path.split(output_dir)[-1]
  335. #load images
  336. t1_subj_hdr = nb.load(t1_subj_bias_corr)
  337. t1_subj = t1_subj_hdr.get_data()
  338. t2_subj = nb.load(t2_subj_bias_corr).get_data()
  339. t1_mni = nb.load(t1_mni_bias_corr).get_data()
  340. t2_mni = nb.load(t2_mni_bias_corr).get_data()
  341. #Load masks
  342. eye_subj_mask = nb.load(eye_subj_mask).get_data().astype(bool)
  343. temp_bone_subj_mask = nb.load(temp_bone_subj_mask).get_data().astype(bool)
  344. brain_subj_mask = nb.load(brain_subj_mask).get_data().astype(bool)
  345. eye_mni_mask = nb.load(eye_mni_mask).get_data().astype(bool)
  346. temp_bone_mni_mask = nb.load(temp_bone_mni_mask).get_data().astype(bool)
  347. brain_mni_mask = nb.load(brain_mni_mask).get_data().astype(bool)
  348. #Extract stats
  349. t1_mni_eye = t1_mni[eye_mni_mask]
  350. t1_mni_eye_stat = stats.mode(t1_mni_eye)[0][0]
  351. t1_mni_temp = t1_mni[temp_bone_mni_mask]
  352. t1_mni_temp_stat = stats.mode(t1_mni_temp)[0][0]
  353. t2_mni_eye = t2_mni[eye_mni_mask]
  354. t2_mni_eye_stat = stats.mode(t2_mni_eye)[0][0]
  355. t2_mni_temp =t2_mni[temp_bone_mni_mask]
  356. t2_mni_temp_stat = stats.mode(t2_mni_temp)[0][0]
  357. print('\nMNI mask values:\nT1 eye = {} T1 temp bone = {}\nT2 eye = {} T2 temp bone = {}\n'.format(t1_mni_eye_stat, t1_mni_temp_stat, t2_mni_eye_stat, t2_mni_temp_stat))
  358. t1_subj_eye = t1_subj[eye_subj_mask]
  359. t1_subj_eye_stat = stats.mode(t1_subj_eye)[0][0]
  360. t1_subj_temp = t1_subj[temp_bone_subj_mask]
  361. t1_subj_temp_stat = stats.mode(t1_subj_temp)[0][0]
  362. t2_subj_eye = t2_subj[eye_subj_mask]
  363. t2_subj_eye_stat = stats.mode(t2_subj_eye)[0][0]
  364. t2_subj_temp =t2_subj[temp_bone_subj_mask]
  365. t2_subj_temp_stat = stats.mode(t2_subj_temp)[0][0]
  366. print('\n{} mask values:\nT1 eye = {} T1 temp bone = {}\nT2 eye = {} T2 temp bone = {}'.format(subj, t1_subj_eye_stat, t1_subj_temp_stat, t2_subj_eye_stat, t2_subj_temp_stat))
  367. with open(os.path.join(output_dir, subj + '_mask_values.txt'), 'w') as text_file:
  368. text_file.write('{} mask values:\nT1 eye = {} T1 temp bone = {}\nT2 eye = {} T2 temp bone = {}'.format(subj, t1_subj_eye_stat, t1_subj_temp_stat, t2_subj_eye_stat, t2_subj_temp_stat))
  369. #FOR FUTURE: SPLIT INTO 2 FUNCTIONS
  370. #Shorten linear equation for easier troubleshooting
  371. t1_a = (t1_mni_temp_stat - t1_mni_eye_stat) / (t1_subj_temp_stat - t1_subj_eye_stat)
  372. t1_b = ((t1_subj_temp_stat * t1_mni_eye_stat) - (t1_mni_temp_stat * t1_subj_eye_stat)) / (t1_subj_temp_stat - t1_subj_eye_stat)
  373. t2_a = (t2_mni_temp_stat - t2_mni_eye_stat) / (t2_subj_temp_stat - t2_subj_eye_stat)
  374. t2_b = ((t2_subj_temp_stat * t2_mni_eye_stat) - (t2_mni_temp_stat * t2_subj_eye_stat)) / (t2_subj_temp_stat - t2_subj_eye_stat)
  375. #Intensity correction
  376. t1_corr = (t1_a * t1_subj[brain_subj_mask]) + t1_b
  377. t2_corr = (t2_a * t2_subj[brain_subj_mask]) + t2_b
  378. print('\n{} bias corrected T1: {} {}'.format(subj, t1_subj.min(), t1_subj.max()))
  379. print('{} bias corrected T2: {} {}'.format(subj, t1_subj.min(), t1_subj.max()))
  380. print('{} calibrated T1: {} {}'.format(subj, t1_corr.min(), t1_corr.max()))
  381. print('{} calibrated T2:{} {}'.format(subj, t2_corr.min(), t2_corr.max()))
  382. t1_corr_out = np.zeros_like(t1_subj)
  383. t2_corr_out = np.zeros_like(t2_subj)
  384. t1_corr_out[brain_subj_mask] = t1_corr
  385. t2_corr_out[brain_subj_mask] = t2_corr
  386. if output_dir != None:
  387. t1_fn = os.path.join(output_dir, 't1_calibrated.nii.gz')
  388. t2_fn = os.path.join(output_dir, 't2_calibrated.nii.gz')
  389. else:
  390. t1_fn = 't1_calibrated.nii.gz'
  391. t2_fn = 't2_calibrated.nii.gz'
  392. nb.Nifti1Image(t1_corr_out, affine = t1_subj_hdr.affine, header = t1_subj_hdr.header).to_filename(t1_fn)
  393. nb.Nifti1Image(t2_corr_out, affine = t1_subj_hdr.affine, header = t1_subj_hdr.header).to_filename(t2_fn)
  394. return(t1_fn, t2_fn)
  395. ###Myelin Map###
  396. def create_mm_func(corrected_t1, corrected_t2, output_dir):
  397. """
  398. The final step. The hard yard. Creation of the myelin map
  399. through division of two matrices. Calculation of this in the pre-computer
  400. era would have been a nightmare. Thankfully we don't live in those dark times...
  401. Inputs:
  402. corrected_t1 - Bias corrected + calibrated T1 image
  403. corrected_t2 - Bias corrected + calibrated T1 image
  404. Output:
  405. mm_image - The myelin map in subject space.
  406. """
  407. subj = os.path.split(output_dir)[-1]
  408. t1_hdr = nb.load(corrected_t1)
  409. t1_im = t1_hdr.get_data()
  410. t1_mask = t1_im > 0
  411. t2_im = nb.load(corrected_t2).get_data()
  412. t2_mask = t2_im > 0
  413. # n_iters = 1
  414. # print('\nRemoving {} voxels at zero boundaries'.format(1 + n_iters))
  415. # t2_mask = be(t2_mask, iterations = n_iters)
  416. # dif_mask = (t1_mask.astype(int) - t2_mask.astype(int)).astype(bool)
  417. # dif_mask = t1_mask.astype(int) - t2_mask.astype(int)
  418. mm = t1_im[t2_mask] / t2_im[t2_mask]
  419. im_mm = np.zeros_like(t1_im)
  420. im_mm[t2_mask] = mm
  421. im_mm[np.isnan(im_mm)] = 0
  422. im_mm[im_mm < 0] = 0
  423. hi = np.percentile(im_mm.ravel(), [99.95]) #Remove .05% extreme values caused by masking issues
  424. im_mm[im_mm > hi] = 0
  425. print('\n{} myelin map values: \nmin = {}\nmax = {}\nmean = {} ({})\nmedian = {}'.format(subj, im_mm.min(),
  426. im_mm.max(),
  427. im_mm[im_mm > 0].mean(),
  428. np.std(im_mm[im_mm > 0]),
  429. np.median(im_mm[im_mm > 0])))
  430. out_im_fn = os.path.join(output_dir, 'myelin_map_subj.nii.gz')
  431. t1_hdr.header['cal_min'] = im_mm.min()
  432. t1_hdr.header['cal_max'] = im_mm.max()
  433. print('HEADER MIN: {}\nHEADER MAX: {}'.format(t1_hdr.header['cal_min'], t1_hdr.header['cal_max']))
  434. nb.Nifti1Image(im_mm, affine = t1_hdr.affine, header = t1_hdr.header).to_filename(out_im_fn)
  435. return(out_im_fn)
  436. def mm_percentile(mmap, mask, output_dir):
  437. """
  438. Takes the raw myelin map output from create_mm_func and zeros values < 5th percentile and > 9th percentile.
  439. NOTE: This is for visualisation purposes ONLY
  440. """
  441. subj = os.path.split(output_dir)[-1]
  442. basename = os.path.split(mmap)[1].split('.')[0]
  443. outname = basename + '_percentile'
  444. print(subj, outname, output_dir)
  445. print('\nConverting myelin map to percentage for {}'.format(subj))
  446. mmap_hdr = nb.load(mmap)
  447. mmap_data = mmap_hdr.get_data()
  448. mask = nb.load(mask).get_data().astype(bool)
  449. mmap_data = ((mmap_data - mmap_data.min() * (1 - 0)) / (mmap_data.max() - mmap_data.min()) + 0)
  450. out_im_fn = os.path.join(output_dir, outname + '.nii.gz')
  451. mmap_hdr.header['cal_min'] = 0
  452. mmap_hdr.header['cal_max'] = 1
  453. nb.Nifti1Image(mmap_data, affine = mmap_hdr.affine, header = mmap_hdr.header).to_filename(out_im_fn)
  454. return(out_im_fn)
  455. ###FUNCTION FOR PARALLEL PROCESSING###
  456. def myelin_map_proc(subj, n_cores, raw_dir, output_dir, patterns, n_scans, dcm_suffix, fwhm_list):
  457. """
  458. Wrapper function that is needed for parallel processing but can also be used for individual subjects.
  459. Inputs:
  460. subj - name of participant to be processed (str)
  461. n_cores - number of cores to be used for parallel processing (int).
  462. note: for single participants only a single core is used).
  463. raw_dir - path to root directory which houses subj/dicoms (str).
  464. output_dir - path to write output to (note: output_dir is the root. Individual participant directories will be created during the process (str).
  465. patterns - strings for how to recognise T1 and T2 dicom directories with the participant directory (dict).
  466. n_scans - Number of expected dicom files in t1 and t2 directories (dict).
  467. dcm_suffix - Strings of file endings for how to recognise dicom files (str).
  468. fwhm_list - Values for size of smoothing kernel (in mm) (list).
  469. Note: Can be single value or multiple.
  470. Output:
  471. Everything.
  472. """
  473. print('Processing data for subj {}'.format(subj))
  474. try:
  475. os.mkdir(output_dir)
  476. except:
  477. print('Directory {} exists. Not creating'.format(output_dir))
  478. out_subj_dir = os.path.join(output_dir, subj)
  479. print()
  480. try:
  481. os.mkdir(out_subj_dir)
  482. except:
  483. print('Directory {} already exists. Not creating.'.format(out_subj_dir))
  484. print("\nWorking dir: {}".format(out_subj_dir))
  485. ##Read in masks
  486. t1_im_mni_fn = os.path.join('.', 'resources','mni_t1_template.nii.gz')
  487. t2_im_mni_fn = os.path.join('.', 'resources','mni_t2_template.nii.gz')
  488. eye_mni_mask_fn = os.path.join('.', 'resources','mni_eye_mask.nii.gz')
  489. temp_bone_mni_mask_fn = os.path.join('.', 'resources','mni_temp_bone_mask.nii.gz')
  490. brain_mni_mask_fn = os.path.join('.', 'resources','mni_brain_mask.nii.gz')
  491. subj_raw_dirs = os.listdir(os.path.join(raw_dir, subj))
  492. t1_dir = [raw_dir for raw_dir in subj_raw_dirs if patterns[0] in raw_dir][0]
  493. t2_dir = [raw_dir for raw_dir in subj_raw_dirs if patterns[1] in raw_dir][0]
  494. scan_dict = {'t1': glob.glob(os.path.join(raw_dir, subj, t1_dir, '*{}'.format(dcm_suffix))),
  495. 't2': glob.glob(os.path.join(raw_dir, subj, t2_dir, '*{}'.format(dcm_suffix)))
  496. }
  497. #Count number of scans for t1 and t2, print and write to file.
  498. im_count = {k: len(list(scan_dict.values())[n]) for n, k in enumerate(scan_dict)}
  499. print(subj, im_count)
  500. with open(os.path.join(out_subj_dir, subj + '_input_files_n.txt'), 'w') as text_file:
  501. text_file.write('{} - {}'.format(subj, im_count))
  502. #Generate error if data not matching criteria
  503. if len(scan_dict['t1']) < n_scans['t1']:
  504. with open(os.path.join(out_subj_dir, subj + '_t1_error.txt'), 'w') as text_file:
  505. text_file.write('Number of T1 scans < {}}: {}'.format(n_scans['t1'], len(scan_dict['t1'])))
  506. raise ValueError('Error: Number of DICOMS in T1 directory less than expected for subj {}\n# of Dicoms = {}'.format(subj, len(scan_dict['t1'])))
  507. if len(scan_dict['t2']) < n_scans['t2']:
  508. with open(os.path.join(out_subj_dir, subj + '_t2_error.txt'), 'w') as text_file:
  509. text_file.write('Number of T2 scans < {}: {}'.format(n_scans['t2'], len(scan_dict['t2'])))
  510. raise ValueError('Error: Number of DICOMS in T2 directory less than expected for subj {}\n# of Dicoms = {}'.format(subj, len(scan_dict['t2'])))
  511. #Start pipeline
  512. t1_subj_nii, t2_subj_nii = dcm_convert(scan_dict, out_subj_dir)
  513. reg_output = ants_reg(fixed = t1_subj_nii, moving = t1_im_mni_fn, output_dir = out_subj_dir)
  514. mask_list = [eye_mni_mask_fn, temp_bone_mni_mask_fn, brain_mni_mask_fn]
  515. brain_subj_mask, eye_subj_mask, temp_bone_subj_mask, = mask_transform(mask_list = mask_list,
  516. ref = t1_subj_nii,
  517. transmat = reg_output['composite_transform'],
  518. output_dir = out_subj_dir)
  519. # plot_ants_warp(fixed = t1_subj_nii, moving = brain_subj_mask, nslices = 10, output_name = os.path.join(out_subj_dir, 'brain_mask'))
  520. rigid_output = ants_rigid(fixed = t1_subj_nii, moving = t2_subj_nii, output_dir = out_subj_dir)
  521. print(t1_subj_nii, rigid_output['warped_image'])
  522. t1_bias, t2_bias = bias_corr([t1_subj_nii, rigid_output['warped_image']], output_dir = out_subj_dir)
  523. #Calibration - TO DO: Split function into mode calculation + linear correction
  524. print('Brain = {}\nEye = {}\nTemp = {}'.format(brain_subj_mask, eye_subj_mask, temp_bone_subj_mask))
  525. t1_cal, t2_cal = image_calibration(t1_subj_bias_corr = t1_bias,
  526. t2_subj_bias_corr = t2_bias,
  527. t1_mni_bias_corr = t1_im_mni_fn,
  528. t2_mni_bias_corr = t2_im_mni_fn,
  529. eye_subj_mask = eye_subj_mask,
  530. temp_bone_subj_mask = temp_bone_subj_mask,
  531. brain_subj_mask = brain_subj_mask,
  532. eye_mni_mask = eye_mni_mask_fn,
  533. temp_bone_mni_mask = temp_bone_mni_mask_fn,
  534. brain_mni_mask = brain_mni_mask_fn,
  535. output_dir = out_subj_dir)
  536. #Calculate myelin maps
  537. myelin_map_subj = create_mm_func(t1_cal, t2_cal, output_dir = out_subj_dir)
  538. #Warp myelin maps to MNI space
  539. subj2mni_output, subj2mni_im = subj2mni(moving = myelin_map_subj, ref = t1_im_mni_fn, transmat = reg_output['inverse_composite_transform'], output_dir = out_subj_dir)
  540. #Smooth
  541. if len(fwhm_list) > 1:
  542. for im in [myelin_map_subj, subj2mni_im]:
  543. for fwhm in fwhm_list:
  544. smoothed = image_smooth(image_fn = im, fwhm = fwhm, output_dir = out_subj_dir)
  545. else:
  546. for im in [myelin_map_subj, subj2mni_im]:
  547. smoothed = image_smooth(image_fn = im, fwhm = fwhm_list[0], output_dir = out_subj_dir)
  548. #Percentile
  549. mmap_list = glob.glob(os.path.join(out_subj_dir, 'myelin_map*'))
  550. mmap_list = [im for im in mmap_list if 'percentile' not in im]
  551. for im in mmap_list:
  552. if 'mni' in im:
  553. mm_percentile(mmap = im, mask = brain_mni_mask_fn, output_dir = out_subj_dir)
  554. else:
  555. mm_percentile(mmap = im, mask = brain_subj_mask, output_dir = out_subj_dir)
  556. for im in mmap_list:
  557. if 'mni' in im:
  558. mm_percentage(mmap = im, mask = brain_mni_mask_fn, output_dir = out_subj_dir)
  559. else:
  560. mm_percentage(mmap = im, mask = brain_subj_mask, output_dir = out_subj_dir)

myelin_map_funcs.py at commit 17d42f9, no license · at the source

Overview

Authors: Valentina Camera1,2,3, Agnese Tamanti1,4, Silvia Messina2,3, Nicola Dall'Osto1, Teresa Maltempo1,4, Stefano Ziccardi1, Matteo Foschi5,6, Maria Grazia Piscaglia5, Diana Ferraro7, Francesco Crescenzo8, Francesca Rossi8, Albulena Bajrami9, Sabrina Marangoni9, Damiano Marastoni1, Francesca Benedetta Pizzini4, Maria Isabel Leite2,3, Roberta Magliozzi1,10, Patrick Waters11, Massimiliano Calabrese1, Jacqueline Palace2,3, Ruth Geraldes2,3
  1. Department of Neuroscience, Biomedicine and Movement Sciences University of Verona Verona Italy
  2. Nuffield Department of Clinical Neurosciences University of Oxford Oxford UK
  3. Department of Clinical Neurology, John Radcliffe Hospital Oxford University Hospitals Foundation Trust Oxford UK
  4. Department of Engineering for Innovation Medicine University of Verona Verona Italy
  5. Santa Maria Delle Croci Hospital, Department of Neuroscience MS Center Ravenna Italy
  6. Department of Biotechnological and Applied Clinical Sciences University of L'Aquila L'Aquila Italy
  7. Department of Neurosciences Azienda Ospedaliero‐Universitaria di Modena, Baggiovara Civil Hospital Modena Italy
  8. Neurology Unit Mater Salutis Hospital Legnago VR Italy
  9. Neurology Unit Santa Chiara Hospital Trento Italy
  10. Department of Brain Sciences, Faculty of Medicine Imperial College London London UK
  11. Oxford Autoimmune Neurology Diagnostic Laboratory, Nuffield Department of Clinical Neurosciences University of Oxford Oxford UK
Journal: Annals of clinical and translational neurology, article 10.1002/acn3.70469
Dates: received 8 March 2026; accepted 14 June 2026; published online 13 July 2026; in print July 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1002/acn3.70469 · PMID 42444075 · PMCID PMC13394544 · OpenAlex W7168271132
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: structural MRI / diffusion (modality), human (organism), other condition (population), cellular / molecular (subfield)
Methods: Statistics, fMRI & imaging, Preprocessing
Keywords: cognitive impairment, cortex, MOGAD, MTsat, quantitative MRI
Topic: Multiple Sclerosis Research Studies (Pathology and Forensic Medicine, Medicine), according to OpenAlex
Funding: Wellcome Trust
Citations: not cited yet (Europe PMC); 44 references in the paper

Abstract

Objective: To determine whether myelin‐sensitive quantitative MRI reveals microstructural abnormalities in normal‐appearing cortex (NACtx) in myelin oligodendrocyte glycoprotein antibody–associated disease (MOGAD), indicating that conventional MRI underestimates remission residual cortical injury.

Methods: Forty‐two patients with MOGAD in remission and 42 age‐ and sex‐matched healthy controls (HCs) underwent cognitive testing (Rao Brief Repeatable Battery), disability rating (Expanded Disability Status Scale) and 3‐T MRI, including three‐dimensional T1‐weighted, T2‐weighted and double‐inversion‐recovery sequences to identify cortical lesions and delineate NACtx. T1‐ to T2‐weighted ratio z‐scores (T1/T2r), magnetisation transfer ratio (MTR) and magnetisation transfer saturation (MTsat) were derived in global and lobar NACtx; MT imaging was available in 22 patients and 24 controls. Linear mixed‐effects models compared MOGAD with HCs and cortical (history of at least one acute cortical attack) with non‐cortical phenotypes, adjusting for age, sex, site and cortical thickness with false‐discovery‐rate correction.

Results: Sixteen of 42 MOGAD patients had a cortical phenotype; cortical lesions at remission were present in 4 of 42 (9.5%), all with a cortical phenotype. MTsat metric, but not T1/T2r or MTR, detected NACtx alterations in the MOGAD cohort compared with HCs. Cortical MOGAD phenotype showed reduced MTsat in global, frontal, temporal, limbic, hippocampal and insular NACtx regions vs. HCs, whereas non‐cortical MOGAD was similar to HCs. Exploratory analyses suggested lower MTsat in temporal, limbic, hippocampal and insular NACtx regions in cortical MOGAD with cognitive impairment than in cognitively preserved MOGAD.

Interpretation: Myelin‐sensitive MTsat reveals persistent, regionally specific abnormalities in normal‐appearing cortex in cortical MOGAD and is a promising marker of residual cortical damage linked to cognitive dysfunction.

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 1 match between paragraphs and lines of code.

petergoodin/myelin_map

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 17d42f983f7b6da8aecad3013b1ca9c6d7e68774, 11 March 2019
Languages: Python (2), Jupyter (1)
Size: 10 files, 3 scripts
Software Heritage: archived
Found in: the text, “MRI Acquisition and Processing”
Holds: README, 1 notebook
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: ANTs (3 files), Matplotlib (3 files), NiBabel (3 files), Nipype (3 files), NumPy (3 files), SciPy (3 files), seaborn (3 files), statsmodels (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
4 files

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;
  • 3 scripts, each with its path and the digest of its content;
  • 1 match 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 Availability Statement

The data that support the findings of this study are available on request from the corresponding author. The data are not publicly available due to privacy or ethical restrictions.

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 1, 27 September 2026: the first record

Recorded: type, language, journal, pages, dates, 21 authors, 5 keywords, 1 funder, 44 references.

Cite

This paper

Camera, V., Tamanti, A., Messina, S., Dall'Osto, N., Maltempo, T., Ziccardi, S., Foschi, M., Piscaglia, M. G., Ferraro, D., Crescenzo, F., Rossi, F., Bajrami, A., Marangoni, S., Marastoni, D., Pizzini, F. B., Leite, M. I., Magliozzi, R., Waters, P., Calabrese, M., . . . Geraldes, R. (2026). Quantitative MRI Uncovers Subtle Cortical Damage in Myelin Oligodendrocyte Glycoprotein Antibody-Associated Disease. Annals of clinical and translational neurology, 10.1002/acn3.70469. https://doi.org/10.1002/acn3.70469

BibTeX

@article{camera2026quantitative,
author = {Camera, Valentina and Tamanti, Agnese and Messina, Silvia and Dall'Osto, Nicola and Maltempo, Teresa and Ziccardi, Stefano and Foschi, Matteo and Piscaglia, Maria Grazia and Ferraro, Diana and Crescenzo, Francesco and Rossi, Francesca and Bajrami, Albulena and Marangoni, Sabrina and Marastoni, Damiano and Pizzini, Francesca Benedetta and Leite, Maria Isabel and Magliozzi, Roberta and Waters, Patrick and Calabrese, Massimiliano and Palace, Jacqueline and Geraldes, Ruth},
title = {{Quantitative MRI Uncovers Subtle Cortical Damage in Myelin Oligodendrocyte Glycoprotein Antibody-Associated Disease}},
journal = {Annals of clinical and translational neurology},
year = {2026},
month = jul,
pages = {10.1002/acn3.70469},
publisher = {Wiley},
issn = {2328-9503},
doi = {10.1002/acn3.70469},
url = {https://doi.org/10.1002/acn3.70469},
pmid = {42444075},
pmcid = {PMC13394544}
}

RIS

TY - JOUR
AU - Camera, Valentina
AU - Tamanti, Agnese
AU - Messina, Silvia
AU - Dall'Osto, Nicola
AU - Maltempo, Teresa
AU - Ziccardi, Stefano
AU - Foschi, Matteo
AU - Piscaglia, Maria Grazia
AU - Ferraro, Diana
AU - Crescenzo, Francesco
AU - Rossi, Francesca
AU - Bajrami, Albulena
AU - Marangoni, Sabrina
AU - Marastoni, Damiano
AU - Pizzini, Francesca Benedetta
AU - Leite, Maria Isabel
AU - Magliozzi, Roberta
AU - Waters, Patrick
AU - Calabrese, Massimiliano
AU - Palace, Jacqueline
AU - Geraldes, Ruth
TI - Quantitative MRI Uncovers Subtle Cortical Damage in Myelin Oligodendrocyte Glycoprotein Antibody-Associated Disease
T2 - Annals of clinical and translational neurology
J2 - Ann Clin Transl Neurol
PY - 2026
DA - 2026/07/13
SP - 10.1002/acn3.70469
SN - 2328-9503
PB - Wiley
DO - 10.1002/acn3.70469
UR - https://doi.org/10.1002/acn3.70469
LA - en
ER -

CSL-JSON

{
"id": "10.1002/acn3.70469",
"type": "article-journal",
"title": "Quantitative MRI Uncovers Subtle Cortical Damage in Myelin Oligodendrocyte Glycoprotein Antibody-Associated Disease",
"container-title": "Annals of clinical and translational neurology",
"author": [
{
"family": "Camera",
"given": "Valentina"
},
{
"family": "Tamanti",
"given": "Agnese"
},
{
"family": "Messina",
"given": "Silvia"
},
{
"family": "Dall'Osto",
"given": "Nicola"
},
{
"family": "Maltempo",
"given": "Teresa"
},
{
"family": "Ziccardi",
"given": "Stefano"
},
{
"family": "Foschi",
"given": "Matteo"
},
{
"family": "Piscaglia",
"given": "Maria Grazia"
},
{
"family": "Ferraro",
"given": "Diana"
},
{
"family": "Crescenzo",
"given": "Francesco"
},
{
"family": "Rossi",
"given": "Francesca"
},
{
"family": "Bajrami",
"given": "Albulena"
},
{
"family": "Marangoni",
"given": "Sabrina"
},
{
"family": "Marastoni",
"given": "Damiano"
},
{
"family": "Pizzini",
"given": "Francesca Benedetta"
},
{
"family": "Leite",
"given": "Maria Isabel"
},
{
"family": "Magliozzi",
"given": "Roberta"
},
{
"family": "Waters",
"given": "Patrick"
},
{
"family": "Calabrese",
"given": "Massimiliano"
},
{
"family": "Palace",
"given": "Jacqueline"
},
{
"family": "Geraldes",
"given": "Ruth"
}
],
"container-title-short": "Ann Clin Transl Neurol",
"page": "10.1002/acn3.70469",
"DOI": "10.1002/acn3.70469",
"PMID": "42444075",
"PMCID": "PMC13394544",
"ISSN": "2328-9503",
"publisher": "Wiley",
"URL": "https://doi.org/10.1002/acn3.70469",
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
13
]
]
}
}

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-73366-9 [code]
Cortical and white matter myelination proceed in concert during early infancy.
Journal: Nature communications
In common: Nipype, statsmodels, NiBabel, 4 other tools, 3 references
[2] doi:10.1038/s42003-026-10276-y [code]
The cellular correlates and adolescent reorganisation of cortical myelination networks in the common marmoset.
Journal: Communications biology
In common: Nipype, statsmodels, NiBabel, 4 other tools, structural MRI / diffusion, 2 references
[3] doi:10.1371/journal.pbio.3003856 [code]
Aging and metabolism contribute separately to brain-body health.
Journal: PLoS biology
In common: ANTs, statsmodels, NiBabel, 4 other tools, structural MRI / diffusion, 2 references
[4] doi:10.1093/braincomms/fcag129 [code]
Longitudinal changes of choroid plexus volumes and MRI ratios in multiple sclerosis.
Journal: Brain communications
In common: ANTs, NiBabel, SciPy, 1 other tool, structural MRI / diffusion, 4 references
[5] doi:10.1038/s41398-026-04157-5 [code]
Association of glymphatic function with 40-Hz neural oscillations, systemic metabolic markers, and cognitive performance in healthy aging adults: An EEG and MRI study.
Journal: Translational psychiatry
In common: Nipype, ANTs, statsmodels, 5 other tools, structural MRI / diffusion
[6] doi:10.1162/imag.a.1362 [code]
Human fMRI at 11.7T: Assessing feasibility, stability, and reliability on the Iseult scanner.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Nipype, ANTs, statsmodels, 5 other tools
[7] doi:10.1371/journal.pbio.3003755 [code]
Action information is integrated into entorhinal representations of conceptual space and is reflected in eye movements.
Journal: PLoS biology
In common: Nipype, ANTs, statsmodels, 5 other tools
[8] doi:10.1038/s41467-026-71151-2 [code]
Common and distinct neural correlates of social interaction processing and theory of mind in narratives.
Journal: Nature communications
In common: Nipype, ANTs, statsmodels, 5 other tools
[9] doi:10.1002/alz.71649 [code]
Postmortem brain MRI reveals differential associations of subcortical and limbic volumes with cortical thinning and neurodegenerative pathologies.
Journal: Alzheimer's & dementia : the journal of the Alzheimer's Association
In common: ANTs, statsmodels, NiBabel, 4 other tools, structural MRI / diffusion, other condition, cellular / molecular, 1 reference
[10] doi:10.1162/imag.a.1245 [code]
Towards precision EEG connectomics: Evaluating the benefits of dense sampling.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Nipype, ANTs, statsmodels, 5 other tools

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.