OSCR

Frame-wise multi-echo distortion correction for superior functional MRI.

Code ↔ Paper

20 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 20 matches · 2 of them tie a paragraph to a whole file, not to given lines: weak matches, whose lines are not tinted
  1. [1] § Methods › Processing pipeline ↔ me_pipeline/params.py, lines 406–535 · score 0.93 · nuisance regression, FD threshold, Frame censoring, Bias field, N4, FSL
  2. [2] § Methods › Multi-echo distortion correction (MEDIC) › Temporal phase correction ↔ warpkit/unwrap.py, lines 495–579 · score 0.66 · ensure phase unwrapping, magnitude image, minimizes, correlation, temporal, frames
  3. [3] § Results › MEDIC dynamic distortion correction reduces the impact of head motion on functional connectivity estimates ↔ medic_analysis/scripts/paper_figures.py, lines 464–523 · score 0.63 · dorsolateral prefrontal cortex, seed map, low motion, DLPFC, exemplar, functional connectivity
  4. [4] § Methods › Processing pipeline ↔ tools/install_4dfp.sh, lines 89–139 · score 0.61 · step resampling, FSL, tools, MNI152, dfp, threshold
  5. [5] § Methods › Data acquisition › ABCD dataset ↔ me_pipeline/params.py, lines 406–535 · score 0.60 · frame censoring, nuisance, FSL, variables, filtered, motion
  6. [6] § Results › MEDIC distortion correction is superior on local and global anatomical alignment metrics ↔ medic_analysis/scripts/paper_figures.py, lines 1280–1402 · score 0.60 · T2w NMI, global metrics, T1w, correlation, alignment, TOPUP
  7. [7] § Results › MEDIC dynamic distortion correction improves functional connectivity in pediatric populations ↔ medic_analysis/scripts/paper_figures.py, lines 772–846 · score 0.59 · occipital cortex, Seed maps, functional connectivity, ABCD, distortion correction, dynamic
  8. [8] § Results › MEDIC frame-wise distortion correction produces superior anatomical alignment ↔ medic_analysis/scripts/paper_figures.py, lines 1094–1178 · score 0.58 · UMinn, WashU, MEDIC TOPUP, arrows, Penn, field maps
  9. [9] § Methods › Multi-echo distortion correction (MEDIC) ↔ warpkit/distortion.py, the whole file · a weak match · score 0.57 · undistorted space, phase images, reconstruction, field map, echo, MEDIC
  10. [10] § Methods › Multi-echo distortion correction (MEDIC) › Phase offset correction and unwrapping ↔ warpkit/distortion.py, the whole file · a weak match · score 0.55 · ROMEO algorithm, Phase unwrapping, echoes, MEDIC
  11. [11] § Results › MEDIC distortion correction is superior on local and global anatomical alignment metrics ↔ medic_analysis/scripts/paper_figures.py, lines 1280–1402 · score 0.55 · Segmentation metrics, alignment metric, AUC, spotlight, T2w, T1w
  12. [12] § Results › MEDIC distortion correction is superior on local and global anatomical alignment metrics ↔ medic_analysis/scripts/alignment_metrics.py, lines 128–210 · score 0.54 · normalized mutual information, alignment metric, gradient, spotlight, correlation, MEDIC
  13. [13] § Methods › Multi-echo distortion correction (MEDIC) › Phase offset correction and unwrapping ↔ warpkit/unwrap.py, lines 82–135 · score 0.54 · unwrapped phase, phase offset, MCPC, echoes
  14. [14] § Results › MEDIC dynamic distortion correction improves functional connectivity in pediatric populations ↔ medic_analysis/scripts/paper_figures.py, lines 772–846 · score 0.52 · occipital cortex, Seed maps, ABCD, dynamic, correlations, TOPUP
  15. [15] § Results › MEDIC distortion correction is superior on local and global anatomical alignment metrics ↔ medic_analysis/scripts/alignment_metrics.py, lines 128–210 · score 0.52 · normalized mutual information, NMI, metrics, gradient, correlation, alignment
  16. [16] § Methods › Multi-echo distortion correction (MEDIC) › Weighted field map computation ↔ warpkit/unwrap.py, lines 495–579 · score 0.52 · weighted linear regression, model, magnitude, voxels, echo, map
  17. [17] § Results › MEDIC dynamic distortion correction reduces the impact of head motion on functional connectivity estimates ↔ medic_analysis/scripts/paper_figures.py, lines 464–523 · score 0.52 · prefrontal cortex, low motion, DLPFC, Functional connectivity, distortion corrected, seeds
  18. [18] § Results › MEDIC captures magnetic field changes due to head motion ↔ medic_analysis/scripts/paper_figures.py, lines 1581–1706 · score 0.51 · motion parameters, head position, neutral, Rotation, field map, dynamic
  19. [19] § Results › MEDIC frame-wise distortion correction produces superior anatomical alignment ↔ medic_analysis/scripts/paper_figures.py, lines 1094–1178 · score 0.51 · UMinn, WashU, Penn, TOPUP, MEDIC
  20. [20] § Methods › Multi-echo distortion correction (MEDIC) › Displacement field inversion ↔ include/itk/itkModifiedInvertDisplacementFieldImageFilter.h, lines 44–179 · score 0.51 · Displacement field, ITK, inverted, inverse, space

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,752 lines · 61 KB · MIT · 9 matches

  1. """Main script for generating paper figures.
  2. See `paper_figures --help` for more information.
  3. """
  4. import argparse
  5. import json
  6. import os
  7. from pathlib import Path
  8. import matplotlib as mpl
  9. import matplotlib.patches as patches
  10. import matplotlib.pyplot as plt
  11. import nibabel as nib
  12. import numpy as np
  13. import pandas as pd
  14. import seaborn as sns
  15. from matplotlib.gridspec import GridSpec, GridSpecFromSubplotSpec
  16. from nilearn.plotting.cm import _cmap_d as nilearn_cmaps
  17. from PIL import Image
  18. from scipy.stats import ttest_rel
  19. from skimage.exposure import equalize_hist
  20. from medic_analysis.common.figures import data_plotter, hz_limits_to_mm, render_dynamic_figure
  21. from . import FIGURES_DIR, MM_TO_INCHES
  22. # Set global seaborn figure settings
  23. GLOBAL_SETTINGS = {
  24. "font": "Satoshi",
  25. "font_scale": 1,
  26. "palette": "pastel",
  27. "style": "white",
  28. "rc": {
  29. "figure.dpi": 150,
  30. "figure.titlesize": 7,
  31. "font.size": 7,
  32. "axes.titlesize": 6,
  33. "axes.titlepad": 0,
  34. "axes.labelsize": 6,
  35. "axes.labelpad": 0,
  36. "axes.linewidth": 0.5,
  37. "legend.title_fontsize": 6,
  38. "legend.fontsize": 6,
  39. "xtick.labelsize": 6,
  40. "xtick.major.pad": 1,
  41. "xtick.major.size": 1,
  42. "ytick.labelsize": 6,
  43. "ytick.major.pad": 1,
  44. "ytick.major.size": 1,
  45. "xtick.major.width": 0.5,
  46. "ytick.major.width": 0.5,
  47. "xtick.minor.width": 0.5,
  48. "ytick.minor.width": 0.5,
  49. },
  50. }
  51. sns.set_theme(**GLOBAL_SETTINGS)
  52. LOWER_FONT_SIZE = 5
  53. # Default paths for data
  54. DATA_DIR = Path(__file__).resolve().parent.parent.parent / "data"
  55. # Head position figure
  56. FIGURE1_DATA = "/home/usr/vana/GMT2/Andrew/HEADPOSITIONSUSTEST"
  57. # Concatenated head position figure
  58. FIGURE2_DATA = "/home/usr/vana/GMT2/Andrew/HEADPOSITIONCAT"
  59. # Group Template Analysis
  60. FIGURE3_DATA = str(DATA_DIR)
  61. # Alignment and Field map Comparison
  62. FIGURE4_DATA = str(DATA_DIR)
  63. # Spotlight Analysis figure
  64. FIGURE5_DATA = str(DATA_DIR)
  65. # Alignment metrics
  66. FIGURE6_DATA = str(DATA_DIR / "alignment_metrics.csv")
  67. # Field map metrics
  68. FIGURE7_DATA = str(DATA_DIR)
  69. # tSNR figure
  70. FIGURE10_DATA = str(DATA_DIR / "tsnr.csv")
  71. # dynamic field map videos
  72. FIGURE100_DATA = "/home/usr/vana/GMT2/Andrew/HEADPOSITIONSUSTEST/derivatives"
  73. AA_DATA_DIR = Path("/data/Daenerys/ASD_ADHD/NP1173/derivatives/me_pipeline2")
  74. WASHU_DATA_DIR = Path("/net/10.20.145.34/DOSENBACH02/GMT2/Andrew/SLICETEST/derivatives/me_pipeline")
  75. PENN_DATA_DIR = Path("/net/10.20.145.34/DOSENBACH02/GMT2/Andrew/UPenn/derivatives/me_pipeline")
  76. MINN_DATA_DIR = Path("/net/10.20.145.34/DOSENBACH02/GMT2/Andrew/UMinn/derivatives")
  77. MINN_DATA_DIR2 = Path("/data/nil-bluearc/GMT/Laumann/Pilot_ME_res/BIO10001/bids/derivatives/me_pipeline")
  78. def plot_box_plot(data, variable, label, ax):
  79. p = sns.color_palette("pastel")
  80. subdata = (
  81. data[[f"{variable}_medic", f"{variable}_topup"]]
  82. .rename(columns={f"{variable}_medic": "MEDIC", f"{variable}_topup": "TOPUP"})
  83. .melt(var_name=label)
  84. )
  85. sb = sns.boxplot(
  86. data=subdata,
  87. x="value",
  88. y=label,
  89. order=["MEDIC", "TOPUP"],
  90. ax=ax,
  91. fliersize=1,
  92. linewidth=0.5,
  93. palette=[p[1], p[0]],
  94. )
  95. sb.set_xlabel("")
  96. sb.set_ylabel(label)
  97. ax.tick_params(axis="x")
  98. ax.tick_params(axis="y")
  99. return sb
  100. def draw_seed(ax, x, y, radius=30, fc="black", ec="white", linewidth=1, zorder=3):
  101. dcoords = ax.transAxes.transform((x, y))
  102. ncoords = ax.transData.inverted().transform(dcoords)
  103. ax.add_patch(patches.Circle(ncoords, radius, fc=fc, ec=ec, linewidth=linewidth, zorder=zorder))
  104. def data_to_ax(ax, corrds):
  105. return ax.transAxes.inverted().transform(ax.transData.transform(corrds))
  106. def draw_arrow(ax, loc1, loc2, color="red", linewidth=1, head_width=2, head_length=4):
  107. dloc1 = ax.transAxes.transform(loc1)
  108. nloc1 = ax.transData.inverted().transform(dloc1)
  109. dloc2 = ax.transAxes.transform(loc2)
  110. nloc2 = ax.transData.inverted().transform(dloc2)
  111. ax.add_patch(
  112. patches.FancyArrowPatch(
  113. nloc1,
  114. nloc2,
  115. arrowstyle="-|>,head_width={},head_length={}".format(head_width, head_length),
  116. color=color,
  117. linewidth=linewidth,
  118. )
  119. )
  120. # figure 1
  121. def head_position_fieldmap(data):
  122. mpl.rcParams["axes.titlesize"] = 7
  123. # Get the data
  124. output_dir = Path(data) / "derivatives"
  125. raw_func_path = (
  126. Path("/home/usr/vana/GMT2/Andrew/HEADPOSITIONCAT")
  127. / "sub-MSCHD02"
  128. / "ses-01"
  129. / "func"
  130. / "sub-MSCHD02_ses-01_task-rest_run-01_echo-1_part-mag_bold.nii.gz"
  131. )
  132. # create a list of expected labels for each run
  133. labels = [
  134. "Neutral",
  135. "+Z Rotation",
  136. "-Z Rotation",
  137. "+X Rotation",
  138. "-X Rotation",
  139. "+Y Rotation",
  140. "-Y Rotation",
  141. "Neutral to +Z Rotation",
  142. "Neutral to -Z Rotation",
  143. "Neutral to +X Rotation",
  144. "Neutral to -X Rotation",
  145. "Neutral to +Y Rotation",
  146. "Neutral to -Y Rotation",
  147. "Neutral to -Z Translation",
  148. "-Z Translation",
  149. ]
  150. # indices for run
  151. static_head_position_run_idx = [0, 1, 2, 3, 4, 5, 6, 14]
  152. # load raw_func data
  153. raw_func = nib.load(raw_func_path)
  154. # Figure 1 - Head Rotation Data
  155. # load field map files
  156. medic_fieldmaps = Path(output_dir) / "fieldmaps" / "medic_aligned"
  157. # load topup field map in neutral position as reference
  158. topup_fieldmap = nib.load(Path(output_dir) / "fieldmaps" / "topup" / "run01" / "fout.nii.gz").get_fdata()
  159. # load static field map runs
  160. static_fieldmaps = []
  161. for idx in static_head_position_run_idx:
  162. run = idx + 1
  163. static_fieldmaps.append(nib.load(medic_fieldmaps / f"run{run:02d}" / "fmap.nii.gz").dataobj)
  164. # load mask
  165. mask = nib.load(Path(output_dir) / "references" / "me_epi_ref_bet_mask.nii.gz").get_fdata()
  166. # plot range
  167. vlims = (-50, 50)
  168. f_topup = plt.figure(figsize=(90 * MM_TO_INCHES, 30 * MM_TO_INCHES), layout="constrained")
  169. gsm = GridSpec(
  170. 1,
  171. 3,
  172. left=0.025,
  173. right=0.975,
  174. bottom=0.025,
  175. top=0.975,
  176. hspace=0.03,
  177. width_ratios=[72, 110, 110],
  178. )
  179. topup_fieldmap = topup_fieldmap
  180. fmin = topup_fieldmap.min()
  181. fmax = topup_fieldmap.max()
  182. axes_list = []
  183. for j in range(3):
  184. axes_list.append(f_topup.add_subplot(gsm[j]))
  185. data_plotter(
  186. [topup_fieldmap],
  187. vmin=fmin,
  188. vmax=fmax,
  189. colormaps="gray",
  190. figure=f_topup,
  191. axes_list=axes_list,
  192. )
  193. sbs = axes_list
  194. sbs[0].set_title(r"TOPUP field map for High motion data", pad=4, weight="normal", loc="left")
  195. f_topup.savefig(FIGURES_DIR / "topup_fmap.png", dpi=300)
  196. # plot static field maps
  197. f0 = plt.figure(figsize=(180 * MM_TO_INCHES, 130 * MM_TO_INCHES), layout="constrained")
  198. # grid spec for head motion images
  199. gsm = GridSpec(
  200. 3,
  201. 7,
  202. left=0.025,
  203. right=0.975,
  204. bottom=0.625,
  205. top=0.96,
  206. wspace=0.04,
  207. hspace=0.03,
  208. height_ratios=[110, 72, 72],
  209. )
  210. # plot movement data
  211. # get min max
  212. func_min = raw_func.dataobj[..., 0].min()
  213. func_max = raw_func.dataobj[..., 0].max()
  214. # create subplots from gridspec
  215. axes_list = []
  216. for i in range(7):
  217. for j in range(3):
  218. axes_list.append(f0.add_subplot(gsm[j, i]))
  219. # plot data
  220. data_plotter( # f0.suptitle("Motion-dependent field map differences (Position - Neutral Position)")
  221. [
  222. raw_func.dataobj[..., 50],
  223. raw_func.dataobj[..., 150],
  224. raw_func.dataobj[..., 250],
  225. raw_func.dataobj[..., 350],
  226. raw_func.dataobj[..., 450],
  227. raw_func.dataobj[..., 550],
  228. raw_func.dataobj[..., 650],
  229. ],
  230. vmin=func_min,
  231. vmax=func_max,
  232. colormaps="gray",
  233. figure=f0,
  234. axes_list=axes_list,
  235. )
  236. sbs = axes_list
  237. sbs[2].set_xlabel("50", labelpad=2)
  238. sbs[20].set_xlabel("650", labelpad=2)
  239. sbs[11].set_xlabel("Frame", labelpad=2)
  240. sbs[0].set_title(r"$\bf{a}$ Functional MRI timeseries: High motion", pad=4, weight="normal", loc="left")
  241. # draw arrow line
  242. f0.canvas.draw()
  243. start = sbs[2].xaxis.label.get_window_extent()
  244. middle = sbs[11].xaxis.label.get_window_extent()
  245. end = sbs[20].xaxis.label.get_window_extent()
  246. # transform to figure coordinates
  247. start = start.transformed(f0.transFigure.inverted())
  248. middle = middle.transformed(f0.transFigure.inverted())
  249. end = end.transformed(f0.transFigure.inverted())
  250. # get the midpoints
  251. start = np.average(start.get_points(), axis=0)
  252. middle = np.average(middle.get_points(), axis=0)
  253. end = np.average(end.get_points(), axis=0)
  254. arrow1 = patches.FancyArrowPatch(
  255. start + np.array([0.01, 0]),
  256. middle - np.array([0.02, 0]),
  257. arrowstyle="-",
  258. color="black",
  259. linewidth=0.5,
  260. )
  261. arrow2 = patches.FancyArrowPatch(
  262. middle + np.array([0.02, 0]),
  263. end - np.array([0.01, 0]),
  264. arrowstyle="-|>,head_width=2,head_length=4",
  265. color="black",
  266. linewidth=0.5,
  267. )
  268. f0.add_artist(arrow1)
  269. f0.add_artist(arrow2)
  270. # get bounding box of last image
  271. bbox = sbs[-1].get_window_extent()
  272. # transform to figure coordinates
  273. bbox = bbox.transformed(f0.transFigure.inverted())
  274. # create a grid spec for the figure
  275. bottom = 0.025
  276. top = 0.575
  277. left_edge_1 = 0.09
  278. right_edge_2 = bbox.x1
  279. pad = 0.02
  280. width = (right_edge_2 - left_edge_1 - pad) / 2
  281. right_edge_1 = left_edge_1 + width
  282. left_edge_2 = right_edge_1 + pad
  283. gs0 = GridSpec(1, 1, left=0.03, right=0.09, bottom=bottom, top=top)
  284. gs_bar = GridSpecFromSubplotSpec(1, 3, wspace=0, hspace=0, width_ratios=[2, 1, 6], subplot_spec=gs0[:, :])
  285. gs1 = GridSpec(
  286. 3,
  287. 3,
  288. left=left_edge_1,
  289. right=right_edge_1,
  290. bottom=bottom,
  291. top=top,
  292. hspace=0.025,
  293. wspace=0.025,
  294. width_ratios=[72, 110, 110],
  295. )
  296. gs2 = GridSpec(
  297. 3,
  298. 3,
  299. left=left_edge_2,
  300. right=right_edge_2,
  301. bottom=bottom,
  302. top=top,
  303. hspace=0.025,
  304. wspace=0.025,
  305. width_ratios=[72, 110, 110],
  306. )
  307. # create subplots
  308. cbar_ax = f0.add_subplot(gs_bar[1])
  309. axes_list = []
  310. for i in range(3):
  311. for j in range(3):
  312. axes_list.append(f0.add_subplot(gs1[i, j]))
  313. for j in range(3):
  314. axes_list.append(f0.add_subplot(gs2[i, j]))
  315. # plot the data
  316. data_plotter(
  317. [
  318. (static_fieldmaps[1][..., 0] - static_fieldmaps[0][..., 0]) * mask,
  319. (static_fieldmaps[2][..., 0] - static_fieldmaps[0][..., 0]) * mask,
  320. (static_fieldmaps[3][..., 0] - static_fieldmaps[0][..., 0]) * mask,
  321. (static_fieldmaps[4][..., 0] - static_fieldmaps[0][..., 0]) * mask,
  322. (static_fieldmaps[5][..., 0] - static_fieldmaps[0][..., 0]) * mask,
  323. (static_fieldmaps[6][..., 0] - static_fieldmaps[0][..., 0]) * mask,
  324. ],
  325. colorbar=True,
  326. colorbar_alt_range=True,
  327. figure=f0,
  328. vmin=vlims[0],
  329. vmax=vlims[1],
  330. axes_list=axes_list,
  331. cbar_ax=cbar_ax,
  332. )
  333. sbs = axes_list
  334. sbs[0].set_title(r"$\bf{b}$ " + f"{labels[1]} (15.0 deg)", pad=4, weight="normal", loc="left")
  335. sbs[3].set_title(r"$\bf{c}$ " + f"{labels[2]} (9.8 deg)", pad=4, weight="normal", loc="left")
  336. sbs[6].set_title(r"$\bf{d}$ " + f"{labels[3]} (10.6 deg)", pad=4, weight="normal", loc="left")
  337. sbs[9].set_title(r"$\bf{e}$ " + f"{labels[4]} (13.7 deg)", pad=4, weight="normal", loc="left")
  338. sbs[12].set_title(r"$\bf{f}$ " + f"{labels[5]} (10.8 deg)", pad=4, weight="normal", loc="left")
  339. sbs[15].set_title(r"$\bf{g}$ " + f"{labels[6]} (8.6 deg)", pad=4, weight="normal", loc="left")
  340. f0.savefig(FIGURES_DIR / "fieldmap_differences.png", dpi=300)
  341. current_dir = os.getcwd()
  342. os.chdir(FIGURES_DIR)
  343. Path("figure1.png").unlink(missing_ok=True)
  344. Path("figure1.png").symlink_to("fieldmap_differences.png")
  345. os.chdir(current_dir)
  346. sns.set_theme(**GLOBAL_SETTINGS)
  347. # figure 2
  348. def head_concatenation(data):
  349. # get dataset
  350. dataset = Path(data)
  351. # load medic and topup workbench screenshots
  352. medic_scan_path = dataset / "medic_scan.png"
  353. topup_scan_path = dataset / "topup_scan.png"
  354. truth_scan_path = dataset / "truth_scan.png"
  355. medic_dlpfc_path = dataset / "medic_dlpfc.png"
  356. topup_dlpfc_path = dataset / "topup_dlpfc.png"
  357. truth_dlpfc_path = dataset / "truth_dlpfc.png"
  358. medic_occipital_path = dataset / "medic_occipital.png"
  359. topup_occipital_path = dataset / "topup_occipital.png"
  360. truth_occipital_path = dataset / "truth_occipital.png"
  361. # load data
  362. clip1 = 50
  363. clip2 = 60
  364. clipy1 = 150
  365. clipy2 = 200
  366. medic_dlpfc = np.array(Image.open(medic_dlpfc_path))
  367. medic_dlpfc_left = medic_dlpfc[clipy1:-clipy2, clip1 : medic_dlpfc.shape[1] // 2 - clip2]
  368. medic_dlpfc_right = medic_dlpfc[clipy1:-clipy2, clip2 + medic_dlpfc.shape[1] // 2 : -clip1]
  369. medic_dlpfc = np.concatenate([medic_dlpfc_left, medic_dlpfc_right], axis=1)
  370. topup_dlpfc = np.array(Image.open(topup_dlpfc_path))
  371. topup_dlpfc_left = topup_dlpfc[clipy1:-clipy2, clip1 : topup_dlpfc.shape[1] // 2 - clip2]
  372. topup_dlpfc_right = topup_dlpfc[clipy1:-clipy2, clip2 + topup_dlpfc.shape[1] // 2 : -clip1]
  373. topup_dlpfc = np.concatenate([topup_dlpfc_left, topup_dlpfc_right], axis=1)
  374. truth_dlpfc = np.array(Image.open(truth_dlpfc_path))
  375. truth_dlpfc_left = truth_dlpfc[clipy1:-clipy2, clip1 : truth_dlpfc.shape[1] // 2 - clip2]
  376. truth_dlpfc_right = truth_dlpfc[clipy1:-clipy2, clip2 + truth_dlpfc.shape[1] // 2 : -clip1]
  377. truth_dlpfc = np.concatenate([truth_dlpfc_left, truth_dlpfc_right], axis=1)
  378. medic_occipital = np.array(Image.open(medic_occipital_path))
  379. medic_occipital_left = medic_occipital[clipy1:-clipy2, clip1 : medic_occipital.shape[1] // 2 - clip2]
  380. medic_occipital_right = medic_occipital[clipy1:-clipy2, clip2 + medic_occipital.shape[1] // 2 : -clip1]
  381. medic_occipital = np.concatenate([medic_occipital_left, medic_occipital_right], axis=1)
  382. topup_occipital = np.array(Image.open(topup_occipital_path))
  383. topup_occipital_left = topup_occipital[clipy1:-clipy2, clip1 : topup_occipital.shape[1] // 2 - clip2]
  384. topup_occipital_right = topup_occipital[clipy1:-clipy2, clip2 + topup_occipital.shape[1] // 2 : -clip1]
  385. topup_occipital = np.concatenate([topup_occipital_left, topup_occipital_right], axis=1)
  386. truth_occipital = np.array(Image.open(truth_occipital_path))
  387. truth_occipital_left = truth_occipital[clipy1:-clipy2, clip1 : truth_occipital.shape[1] // 2 - clip2]
  388. truth_occipital_right = truth_occipital[clipy1:-clipy2, clip2 + truth_occipital.shape[1] // 2 : -clip1]
  389. truth_occipital = np.concatenate([truth_occipital_left, truth_occipital_right], axis=1)
  390. medic_scan = np.array(Image.open(medic_scan_path))
  391. medic_scan_left = medic_scan[clipy1:-clipy2, clip1 : medic_scan.shape[1] // 2 - clip2]
  392. medic_scan_right = medic_scan[clipy1:-clipy2, clip2 + medic_scan.shape[1] // 2 : -clip1]
  393. medic_scan = np.concatenate([medic_scan_left, medic_scan_right], axis=1)
  394. topup_scan = np.array(Image.open(topup_scan_path))
  395. topup_scan_left = topup_scan[clipy1:-clipy2, clip1 : topup_scan.shape[1] // 2 - clip2]
  396. topup_scan_right = topup_scan[clipy1:-clipy2, clip2 + topup_scan.shape[1] // 2 : -clip1]
  397. topup_scan = np.concatenate([topup_scan_left, topup_scan_right], axis=1)
  398. truth_scan = np.array(Image.open(truth_scan_path))
  399. truth_scan_left = truth_scan[clipy1:-clipy2, clip1 : truth_scan.shape[1] // 2 - clip2]
  400. truth_scan_right = truth_scan[clipy1:-clipy2, clip2 + truth_scan.shape[1] // 2 : -clip1]
  401. truth_scan = np.concatenate([truth_scan_left, truth_scan_right], axis=1)
  402. # create a figure
  403. f = plt.figure(figsize=(180 * MM_TO_INCHES, 100 * MM_TO_INCHES), layout="constrained")
  404. # create gridspec
  405. gs = GridSpec(
  406. 3,
  407. 4,
  408. left=0.005,
  409. right=0.995,
  410. bottom=0.005,
  411. top=0.975,
  412. wspace=0.15,
  413. hspace=0.01,
  414. width_ratios=[9, 9, 1, 9],
  415. )
  416. gs_cbar = GridSpecFromSubplotSpec(
  417. 3,
  418. 3,
  419. wspace=0,
  420. hspace=0,
  421. width_ratios=[3, 2, 7],
  422. height_ratios=[1, 20, 1],
  423. subplot_spec=gs[:, 2],
  424. )
  425. # plot images
  426. mpl.rcParams["axes.edgecolor"] = "white"
  427. ax_medic_dlpfc = f.add_subplot(gs[0, 0])
  428. ax_medic_dlpfc.imshow(medic_dlpfc)
  429. draw_seed(ax_medic_dlpfc, x=0.13, y=0.62)
  430. ax_medic_dlpfc.set_xticks([])
  431. ax_medic_dlpfc.set_yticks([])
  432. ax_medic_dlpfc.set_title("MEDIC: Dynamic distortion correction", pad=6, loc="center")
  433. medic_title_pos = ax_medic_dlpfc.transAxes.inverted().transform(ax_medic_dlpfc.title.get_window_extent())
  434. ax_medic_dlpfc.text(
  435. 0.5,
  436. 1,
  437. "Exemplar participant",
  438. ha="center",
  439. va="center",
  440. fontsize=5,
  441. transform=ax_medic_dlpfc.transAxes,
  442. )
  443. ax_medic_dlpfc.set_xlabel("Correlation to standard: r = 0.41", labelpad=2)
  444. ax_topup_dlpfc = f.add_subplot(gs[0, 1])
  445. ax_topup_dlpfc.imshow(topup_dlpfc)
  446. draw_seed(ax_topup_dlpfc, x=0.13, y=0.62)
  447. ax_topup_dlpfc.set_xticks([])
  448. ax_topup_dlpfc.set_yticks([])
  449. ax_topup_dlpfc.set_title("TOPUP: Static distortion correction", pad=6, loc="center")
  450. topup_title_pos = ax_topup_dlpfc.transAxes.inverted().transform(ax_topup_dlpfc.title.get_window_extent())
  451. ax_topup_dlpfc.text(
  452. 0.5,
  453. 1,
  454. "Exemplar participant",
  455. ha="center",
  456. va="center",
  457. fontsize=5,
  458. transform=ax_topup_dlpfc.transAxes,
  459. )
  460. ax_topup_dlpfc.set_xlabel("Correlation to standard: r = 0.18", labelpad=2)
  461. ax_truth_dlpfc = f.add_subplot(gs[0, 3])
  462. ax_truth_dlpfc.imshow(truth_dlpfc)
  463. draw_seed(ax_truth_dlpfc, x=0.13, y=0.62)
  464. ax_truth_dlpfc.set_xticks([])
  465. ax_truth_dlpfc.set_yticks([])
  466. ax_truth_dlpfc.set_title("Standard: Low motion (TOPUP: static)", pad=6, loc="center")
  467. truth_title_pos = ax_truth_dlpfc.transAxes.inverted().transform(ax_truth_dlpfc.title.get_window_extent())
  468. ax_truth_dlpfc.text(
  469. 0.5,
  470. 1,
  471. "Exemplar participant",
  472. ha="center",
  473. va="center",
  474. fontsize=5,
  475. transform=ax_truth_dlpfc.transAxes,
  476. )
  477. ax_pos = ax_medic_dlpfc.get_position()
  478. f.text(
  479. ax_pos.x0,
  480. ax_pos.y1 + 0.06,
  481. r"$\bf{a}$ Functional connectivity (FC) seed maps: Dorsolateral prefrontal cortex (DLPFC)",
  482. ha="left",
  483. va="center",
  484. )
  485. ax_medic_occiptal = f.add_subplot(gs[1, 0])
  486. ax_medic_occiptal.imshow(medic_occipital)
  487. draw_seed(ax_medic_occiptal, x=0.44, y=0.32)
  488. ax_medic_occiptal.set_xticks([])
  489. ax_medic_occiptal.set_yticks([])
  490. ax_medic_occiptal.set_xlabel("Correlation to standard: r = 0.53", labelpad=2)
  491. f.text(
  492. medic_title_pos[0, 0],
  493. medic_title_pos[0, 1],
  494. "MEDIC",
  495. ha="left",
  496. va="bottom",
  497. fontsize=6,
  498. transform=ax_medic_occiptal.transAxes,
  499. )
  500. ax_topup_occipital = f.add_subplot(gs[1, 1])
  501. ax_topup_occipital.imshow(topup_occipital)
  502. draw_seed(ax_topup_occipital, x=0.44, y=0.32)
  503. ax_topup_occipital.set_xticks([])
  504. ax_topup_occipital.set_yticks([])
  505. ax_topup_occipital.set_xlabel("Correlation to standard: r = 0.38", labelpad=2)
  506. f.text(
  507. topup_title_pos[0, 0],
  508. topup_title_pos[0, 1],
  509. "TOPUP",
  510. ha="left",
  511. va="bottom",
  512. fontsize=6,
  513. transform=ax_topup_occipital.transAxes,
  514. )
  515. ax_truth_occipital = f.add_subplot(gs[1, 3])
  516. ax_truth_occipital.imshow(truth_occipital)
  517. draw_seed(ax_truth_occipital, x=0.44, y=0.32)
  518. ax_truth_occipital.set_xticks([])
  519. ax_truth_occipital.set_yticks([])
  520. f.text(
  521. truth_title_pos[0, 0],
  522. truth_title_pos[0, 1],
  523. "Standard",
  524. ha="left",
  525. va="bottom",
  526. fontsize=6,
  527. transform=ax_truth_occipital.transAxes,
  528. )
  529. ax_pos = ax_medic_occiptal.get_position()
  530. f.text(
  531. ax_pos.x0,
  532. ax_pos.y1 + 0.06,
  533. r"$\bf{b}$ Functional connectivity (FC) seed maps: Occipital cortex (extrastriate visual)",
  534. ha="left",
  535. va="center",
  536. )
  537. ax_medic_scan = f.add_subplot(gs[2, 0])
  538. ax_medic_scan.imshow(medic_scan)
  539. draw_seed(ax_medic_scan, x=0.20, y=0.37)
  540. ax_medic_scan.set_xticks([])
  541. ax_medic_scan.set_yticks([])
  542. ax_medic_scan.set_xlabel("Correlation to standard: r = 0.23", labelpad=2)
  543. f.text(
  544. medic_title_pos[0, 0],
  545. medic_title_pos[0, 1],
  546. "MEDIC",
  547. ha="left",
  548. va="bottom",
  549. fontsize=6,
  550. transform=ax_medic_scan.transAxes,
  551. )
  552. ax_topup_scan = f.add_subplot(gs[2, 1])
  553. ax_topup_scan.imshow(topup_scan)
  554. draw_seed(ax_topup_scan, x=0.20, y=0.37)
  555. ax_topup_scan.set_xticks([])
  556. ax_topup_scan.set_yticks([])
  557. ax_topup_scan.set_xlabel("Correlation to standard: r = 0.18", labelpad=2)
  558. f.text(
  559. topup_title_pos[0, 0],
  560. topup_title_pos[0, 1],
  561. "TOPUP",
  562. ha="left",
  563. va="bottom",
  564. fontsize=6,
  565. transform=ax_topup_scan.transAxes,
  566. )
  567. ax_truth_scan = f.add_subplot(gs[2, 3])
  568. ax_truth_scan.imshow(truth_scan)
  569. draw_seed(ax_truth_scan, x=0.20, y=0.37)
  570. ax_truth_scan.set_xticks([])
  571. ax_truth_scan.set_yticks([])
  572. f.text(
  573. truth_title_pos[0, 0],
  574. truth_title_pos[0, 1],
  575. "Standard",
  576. ha="left",
  577. va="bottom",
  578. fontsize=6,
  579. transform=ax_truth_scan.transAxes,
  580. )
  581. ax_pos = ax_medic_scan.get_position()
  582. f.text(
  583. ax_pos.x0,
  584. ax_pos.y1 + 0.06,
  585. r"$\bf{c}$ Functional connectivity (FC) seed maps: Somato-cognitive action network (SCAN)",
  586. ha="left",
  587. va="center",
  588. )
  589. mpl.rcParams["axes.edgecolor"] = "black"
  590. # create colorbar
  591. cbar_ax = f.add_subplot(gs_cbar[1, 1])
  592. pl = cbar_ax.imshow(
  593. np.array([[-0.6, 0.6], [0.6, -0.6]]),
  594. vmin=-0.6,
  595. vmax=0.6,
  596. aspect="auto",
  597. cmap=nilearn_cmaps["roy_big_bl"],
  598. )
  599. cbar = f.colorbar(
  600. mappable=pl,
  601. cax=cbar_ax,
  602. location="left",
  603. orientation="vertical",
  604. ticks=[-0.6, -0.3, 0, 0.3, 0.6],
  605. )
  606. cbar.ax.yaxis.set_ticks_position("right")
  607. cbar.ax.invert_yaxis()
  608. # create axis for colorbar
  609. cbar.ax.set_ylabel("Functional Connectivity z(r)", labelpad=2)
  610. # # for computing correlations
  611. # # load dconn data for medic and topup
  612. # low_dconn = nib.load(
  613. # Path("/net/10.20.145.34/DOSENBACH02/GMT2/Andrew/HEADPOSITIONCAT/Pilot_ME_res/cifti_correlation_concat")
  614. # / "MSCHD02_10run_concat_ME_MNI152_T1_2mm_Swgt_norm_bpss_resid_LR_surf_subcort_32k_fsLR_brainstem_smooth1.7_corr.dconn.nii" # noqa
  615. # )
  616. # medic_dconn = nib.load(
  617. # dataset
  618. # / "derivatives"
  619. # / "me_pipeline"
  620. # / "sub-MSCHD02"
  621. # / "ses-01wNEWPROC"
  622. # / "cifti_correlation"
  623. # / "sub-MSCHD02_b1_MNI152_T1_2mm_Swgt_norm_bpss_resid_LR_surf_subcort_32k_fsLR_brainstem_surfsmooth1.7_subcortsmooth1.7.dconn.nii" # noqa
  624. # )
  625. # topup_dconn = nib.load(
  626. # dataset
  627. # / "derivatives"
  628. # / "me_pipeline"
  629. # / "sub-MSCHD02"
  630. # / "ses-01wTOPUP"
  631. # / "cifti_correlation"
  632. # / "sub-MSCHD02_b1_MNI152_T1_2mm_Swgt_norm_bpss_resid_LR_surf_subcort_32k_fsLR_brainstem_surfsmooth1.7_subcortsmooth1.7.dconn.nii" # noqa
  633. # )
  634. # # get the lower triangle of the dconn data
  635. # print("Loading dconn data...")
  636. # low_dconn_data = low_dconn.dataobj[:59412, :59412][np.tril_indices(59412)]
  637. # medic_dconn_data = medic_dconn.dataobj[:59412, :59412][np.tril_indices(59412)]
  638. # topup_dconn_data = topup_dconn.dataobj[:59412, :59412][np.tril_indices(59412)]
  639. # # remove nans
  640. # low_dconn_data[np.isnan(low_dconn_data)] = 0
  641. # medic_dconn_data[np.isnan(medic_dconn_data)] = 0
  642. # topup_dconn_data[np.isnan(topup_dconn_data)] = 0
  643. # print("Done.")
  644. # # compute correlations
  645. # print("Computing correlations...")
  646. # medic_corr = pearsonr(low_dconn_data, medic_dconn_data)
  647. # print(medic_corr)
  648. # topup_corr = pearsonr(low_dconn_data, topup_dconn_data)
  649. # print(topup_corr)
  650. # print("Done.")
  651. f.savefig(FIGURES_DIR / "head_position_concat.png", dpi=300)
  652. current_dir = os.getcwd()
  653. os.chdir(FIGURES_DIR)
  654. Path("figure2.png").unlink(missing_ok=True)
  655. Path("figure2.png").symlink_to("head_position_concat.png")
  656. os.chdir(current_dir)
  657. sns.set_theme(**GLOBAL_SETTINGS)
  658. # figure 3
  659. def group_template_comparison(data):
  660. aa_dir = Path(data)
  661. with open(aa_dir / "paircorr.json", "r") as f:
  662. data = json.load(f)
  663. # convert to dataframe
  664. df = pd.DataFrame(data)
  665. medic_similarities = df.MEDIC.to_numpy()
  666. topup_similarities = df.TOPUP.to_numpy()
  667. # get where medic is better and topup is better
  668. medic_better = medic_similarities > topup_similarities
  669. topup_better = medic_similarities < topup_similarities
  670. # get similarities where medic is better and topup is better
  671. medic_better_similarities_medic = medic_similarities[medic_better]
  672. topup_better_similarities_medic = topup_similarities[medic_better]
  673. medic_better_similarities_topup = medic_similarities[topup_better]
  674. topup_better_similarities_topup = topup_similarities[topup_better]
  675. # make figure
  676. f = plt.figure(figsize=(180 * MM_TO_INCHES, 128 * MM_TO_INCHES), layout="constrained")
  677. # create grid specs
  678. gs = GridSpec(
  679. 1,
  680. 5,
  681. left=0.005,
  682. right=0.995,
  683. bottom=0.505,
  684. top=0.995,
  685. wspace=0.075,
  686. hspace=0,
  687. width_ratios=[9, 1, 9, 1, 9],
  688. )
  689. gs_cbar = GridSpecFromSubplotSpec(
  690. 3,
  691. 3,
  692. wspace=0,
  693. hspace=0,
  694. width_ratios=[2, 1, 6],
  695. height_ratios=[2, 5, 2],
  696. subplot_spec=gs[3],
  697. )
  698. gs_bot = GridSpec(
  699. 1,
  700. 2,
  701. left=0.04,
  702. right=0.96,
  703. bottom=0.03,
  704. top=0.47,
  705. wspace=0.25,
  706. hspace=0.1,
  707. width_ratios=[1, 2],
  708. )
  709. gs_tstat = GridSpecFromSubplotSpec(1, 2, wspace=0.075, hspace=0, width_ratios=[9, 1], subplot_spec=gs_bot[1])
  710. gs_tstat_cbar = GridSpecFromSubplotSpec(
  711. 3,
  712. 3,
  713. wspace=0,
  714. hspace=0,
  715. width_ratios=[3, 1, 9],
  716. height_ratios=[1, 10, 1],
  717. subplot_spec=gs_tstat[1],
  718. )
  719. # plot surfaces
  720. clip_1 = 145
  721. clip_2 = 145
  722. medic_occipital_path = DATA_DIR / "medic_occipital_20008.png"
  723. medic_occipital = np.array(Image.open(medic_occipital_path))
  724. medic_occipital_left = medic_occipital[:, clip_1 : medic_occipital.shape[1] // 2 - clip_2]
  725. medic_occipital_right = medic_occipital[:, clip_2 + medic_occipital.shape[1] // 2 : -clip_1]
  726. medic_occipital = np.concatenate((medic_occipital_left, medic_occipital_right), axis=1)
  727. topup_occipital_path = DATA_DIR / "topup_occipital_20008.png"
  728. topup_occipital = np.array(Image.open(topup_occipital_path))
  729. topup_occipital_left = topup_occipital[:, clip_1 : topup_occipital.shape[1] // 2 - clip_2]
  730. topup_occipital_right = topup_occipital[:, clip_2 + topup_occipital.shape[1] // 2 : -clip_1]
  731. topup_occipital = np.concatenate((topup_occipital_left, topup_occipital_right), axis=1)
  732. group_template_abcd_path = DATA_DIR / "group_abcd_template_surface.png"
  733. group_template_abcd = np.array(Image.open(group_template_abcd_path))
  734. group_template_abcd_left = group_template_abcd[:, clip_1 : group_template_abcd.shape[1] // 2 - clip_2]
  735. group_template_abcd_right = group_template_abcd[:, clip_2 + group_template_abcd.shape[1] // 2 : -clip_1]
  736. group_template_abcd = np.concatenate((group_template_abcd_left, group_template_abcd_right), axis=1)
  737. mpl.rcParams["axes.edgecolor"] = "white"
  738. mpl.rcParams["font.size"] = 6
  739. axl_medic = f.add_subplot(gs[0])
  740. axl_topup = f.add_subplot(gs[2])
  741. axl_group = f.add_subplot(gs[4])
  742. axl_medic.imshow(medic_occipital)
  743. draw_seed(axl_medic, x=0.48, y=0.7)
  744. axl_medic.set_xticks([])
  745. axl_medic.set_yticks([])
  746. axl_medic.set_xlabel("Correlation to standard: r = 0.44", labelpad=6)
  747. axl_medic.set_title("MEDIC: Dynamic distortion correction", pad=6)
  748. axl_medic.text(0.5, 0.5, "Participant 1", ha="center", va="center", transform=axl_medic.transAxes)
  749. axl_topup.imshow(topup_occipital)
  750. draw_seed(axl_topup, x=0.48, y=0.7)
  751. axl_topup.set_xticks([])
  752. axl_topup.set_yticks([])
  753. axl_topup.set_xlabel("Correlation to standard: r = 0.04", labelpad=6)
  754. axl_topup.set_title("TOPUP: Static distortion correction", pad=6)
  755. axl_topup.text(0.5, 0.5, "Participant 1", ha="center", va="center", transform=axl_topup.transAxes)
  756. axl_group.imshow(group_template_abcd)
  757. draw_seed(axl_group, x=0.48, y=0.7)
  758. axl_group.set_xticks([])
  759. axl_group.set_yticks([])
  760. axl_group.set_title("Group-averaged standard (TOPUP: static)", pad=6)
  761. axl_group.text(0.5, 0.5, "ABCD (N = 3,928)", ha="center", va="center", transform=axl_group.transAxes)
  762. axl_pos = axl_medic.get_position()
  763. f.text(
  764. 0.01,
  765. axl_pos.y1 + 0.075,
  766. r"$\bf{a}$ Functional Connectivity (FC) seed maps: Occipital Cortex",
  767. ha="left",
  768. va="center",
  769. fontsize=7,
  770. )
  771. mpl.rcParams["axes.edgecolor"] = "black"
  772. mpl.rcParams["xtick.labelsize"] = 5
  773. mpl.rcParams["ytick.labelsize"] = 5
  774. cbar_ax = f.add_subplot(gs_cbar[1, 1])
  775. pl = cbar_ax.imshow(
  776. np.array([[-0.5, 0.5], [0.5, -0.5]]),
  777. vmin=-0.5,
  778. vmax=0.5,
  779. aspect="auto",
  780. cmap=nilearn_cmaps["roy_big_bl"],
  781. )
  782. cbar = f.colorbar(
  783. mappable=pl,
  784. cax=cbar_ax,
  785. location="left",
  786. orientation="vertical",
  787. ticks=[-0.5, -0.25, 0, 0.25, 0.5],
  788. )
  789. cbar.ax.yaxis.set_ticks_position("right")
  790. cbar.ax.invert_yaxis()
  791. # create axis for colorbar
  792. cbar.ax.set_ylabel("Functional Connectivity z(r)", labelpad=2)
  793. # plot group similarities
  794. settings = GLOBAL_SETTINGS.copy()
  795. settings["style"] = "darkgrid"
  796. sns.set_theme(**settings)
  797. mpl.rcParams["xtick.major.size"] = 0.5
  798. mpl.rcParams["xtick.major.pad"] = 2
  799. mpl.rcParams["ytick.major.size"] = 0.5
  800. mpl.rcParams["font.size"] = 6
  801. ax1 = f.add_subplot(gs_bot[0])
  802. clr_pal = sns.color_palette("pastel")
  803. sns.scatterplot(topup_better_similarities_topup, medic_better_similarities_topup, s=14, ax=ax1)
  804. sns.scatterplot(topup_better_similarities_medic, medic_better_similarities_medic, s=14, ax=ax1)
  805. sns.scatterplot([0.51], [0.33], s=14, linewidth=0.5, ax=ax1, color=clr_pal[0])
  806. sns.scatterplot([0.32], [0.52], s=14, linewidth=0.5, ax=ax1, color=clr_pal[1])
  807. ax1.text(0.515, 0.33, "Scan", ha="left", va="center", fontsize=5, transform=ax1.transData)
  808. ax1.text(0.325, 0.52, "Scan", ha="left", va="center", fontsize=5, transform=ax1.transData)
  809. ax1.axline((0, 0), slope=1, color="black", linestyle="--", linewidth=1)
  810. ax1.set_xlabel("Correlation (r)", labelpad=-6)
  811. ax1.set_ylabel("Correlation (r)", labelpad=-10)
  812. ax1.set_aspect("equal")
  813. vmax = 0.55
  814. vmin = 0.3
  815. ax1.set_xlim([vmin, vmax])
  816. ax1.set_ylim([vmin, vmax])
  817. ax1.set_xticks(np.arange(vmin, vmax, 0.05))
  818. ax1.set_yticks(np.arange(vmin, vmax, 0.05))
  819. ax1.set_xticklabels(
  820. [f"{x:.2f}" if np.isclose(x, vmin) or np.isclose(x, vmax) else "" for x in np.arange(vmin, vmax, 0.05)]
  821. )
  822. ax1.set_yticklabels([f"{x:.2f}" if np.isclose(x, vmax) else "" for x in np.arange(vmin, vmax, 0.05)])
  823. ax1.text(
  824. 0.075,
  825. 0.95,
  826. "MEDIC more similar to Group Average",
  827. ha="left",
  828. va="center",
  829. transform=ax1.transAxes,
  830. )
  831. ax1.text(
  832. 0.925,
  833. 0.05,
  834. "TOPUP more similar to Group Average",
  835. ha="right",
  836. va="center",
  837. transform=ax1.transAxes,
  838. )
  839. x = np.array([-0.1, 0.7])
  840. y = x
  841. y2 = np.ones(x.shape) * 0.6
  842. y3 = np.zeros(x.shape)
  843. colors = sns.color_palette("pastel")
  844. medic_color = colors[1]
  845. topup_color = colors[0]
  846. ax1.fill_between(x, y, y2, color=medic_color, alpha=0.2)
  847. ax1.fill_between(x, y3, y, color=topup_color, alpha=0.2)
  848. ax1_pos = ax1.get_position()
  849. f.text(
  850. 0.01,
  851. ax1_pos.y1 + 0.075,
  852. r"$\bf{b}$ Whole-brain FC similarity to group-averaged standard",
  853. ha="left",
  854. va="center",
  855. fontsize=7,
  856. )
  857. sns.set_theme(**GLOBAL_SETTINGS)
  858. # plot t-statistic surface
  859. tstat_surface_path = DATA_DIR / "group_tstat_surface.png"
  860. tstat_surface = np.array(Image.open(tstat_surface_path))
  861. tstat_surface_left = tstat_surface[:, clip_1 : tstat_surface.shape[1] // 2 - clip_2]
  862. tstat_surface_right = tstat_surface[:, clip_2 + tstat_surface.shape[1] // 2 : -clip_1]
  863. tstat_surface = np.concatenate((tstat_surface_left, tstat_surface_right), axis=1)
  864. mpl.rcParams["axes.edgecolor"] = "white"
  865. mpl.rcParams["xtick.labelsize"] = 5
  866. mpl.rcParams["ytick.labelsize"] = 5
  867. ax2 = f.add_subplot(gs_tstat[0])
  868. ax2.imshow(tstat_surface)
  869. ax2.set_xticks([])
  870. ax2.set_yticks([])
  871. ax2.text(0.5, 0.5, "N = 185 Scans", ha="center", va="center", transform=ax2.transAxes, fontsize=6)
  872. mpl.rcParams["axes.edgecolor"] = "black"
  873. cbar_ax = f.add_subplot(gs_tstat_cbar[1, 1])
  874. spectral_map = plt.cm.get_cmap("Spectral")
  875. spectral_rmap = spectral_map.reversed()
  876. pl = cbar_ax.imshow(np.array([[-6, 6], [6, -6]]), vmin=-6, vmax=6, aspect="auto", cmap=spectral_rmap)
  877. cbar = f.colorbar(
  878. mappable=pl,
  879. cax=cbar_ax,
  880. location="left",
  881. orientation="vertical",
  882. ticks=[-6, -3, 0, 3, 6],
  883. )
  884. cbar.ax.yaxis.set_ticks_position("right")
  885. cbar.ax.invert_yaxis()
  886. # create axis for colorbar
  887. cbar.ax.set_ylabel("t-statistic", labelpad=2)
  888. ax2_pos = ax2.get_position()
  889. f.text(
  890. ax2_pos.x0,
  891. ax1_pos.y1 + 0.075,
  892. r"$\bf{c}$ Whole-brain FC similarity to group-averaged standard",
  893. ha="left",
  894. va="center",
  895. )
  896. cbar.ax.text(
  897. 0.5,
  898. 1.075,
  899. "MEDIC > TOPUP",
  900. ha="center",
  901. va="center",
  902. transform=cbar.ax.transAxes,
  903. fontsize=6,
  904. )
  905. cbar.ax.text(
  906. 0.5,
  907. -0.075,
  908. "TOPUP > MEDIC",
  909. ha="center",
  910. va="center",
  911. transform=cbar.ax.transAxes,
  912. fontsize=6,
  913. )
  914. # # for computing correlations
  915. # # load dconn data for medic and topup
  916. # group_avg = nib.load("/data/nil-bluearc/GMT/Scott/ABCD/ABCD_4.5k_all.dconn.nii")
  917. # medic_dconn = nib.load(
  918. # AA_DATA_DIR
  919. # / "sub-20008"
  920. # / "ses-51692"
  921. # / "cifti_correlation"
  922. # / "sub-20008_b1_MNI152_T1_2mm_Swgt_norm_bpss_resid_LR_surf_subcort_32k_fsLR_brainstem_surfsmooth1.7_subcortsmooth1.7.dconn.nii" # noqa
  923. # )
  924. # topup_dconn = nib.load(
  925. # AA_DATA_DIR
  926. # / "sub-20008"
  927. # / "ses-51692wTOPUP"
  928. # / "cifti_correlation"
  929. # / "sub-20008_b1_MNI152_T1_2mm_Swgt_norm_bpss_resid_LR_surf_subcort_32k_fsLR_brainstem_surfsmooth1.7_subcortsmooth1.7.dconn.nii" # noqa
  930. # )
  931. # # get the lower triangle of the dconn data
  932. # print("Loading dconn data...")
  933. # group_avg_data = group_avg.dataobj[21891, :59412]
  934. # medic_dconn_data = medic_dconn.dataobj[21891, :59412]
  935. # topup_dconn_data = topup_dconn.dataobj[21891, :59412]
  936. # # remove nans
  937. # group_avg_data[np.isnan(group_avg_data)] = 0
  938. # medic_dconn_data[np.isnan(medic_dconn_data)] = 0
  939. # topup_dconn_data[np.isnan(topup_dconn_data)] = 0
  940. # print("Done.")
  941. # # compute correlations
  942. # print("Computing correlations...")
  943. # medic_corr = pearsonr(group_avg_data, medic_dconn_data)
  944. # print(medic_corr)
  945. # topup_corr = pearsonr(group_avg_data, topup_dconn_data)
  946. # print(topup_corr)
  947. # print("Done.")
  948. f.savefig(FIGURES_DIR / "group_template_compare.png", dpi=300)
  949. current_dir = os.getcwd()
  950. os.chdir(FIGURES_DIR)
  951. Path("figure3.png").unlink(missing_ok=True)
  952. Path("figure3.png").symlink_to("group_template_compare.png")
  953. os.chdir(current_dir)
  954. sns.set_theme(**GLOBAL_SETTINGS)
  955. # figure 4
  956. def fmap_comparison(data_dir):
  957. mpl.rcParams["axes.titlesize"] = 7
  958. mpl.rcParams["axes.labelsize"] = 7
  959. # get data
  960. data_path = Path(data_dir)
  961. # load images
  962. minn_example_medic_path = data_path / "UMinn_medic.png"
  963. minn_example_medic = equalize_hist(np.array(Image.open(minn_example_medic_path)))
  964. minn_example_topup_path = data_path / "UMinn_topup.png"
  965. minn_example_topup = equalize_hist(np.array(Image.open(minn_example_topup_path)))
  966. minn_example_fmap_path = data_path / "UMinn_fmap.png"
  967. minn_example_fmap = np.array(Image.open(minn_example_fmap_path))
  968. penn_example_medic_path = data_path / "Penn_medic.png"
  969. penn_example_medic = equalize_hist(np.array(Image.open(penn_example_medic_path)))
  970. penn_example_topup_path = data_path / "Penn_topup.png"
  971. penn_example_topup = equalize_hist(np.array(Image.open(penn_example_topup_path)))
  972. penn_example_fmap_path = data_path / "Penn_fmap.png"
  973. penn_example_fmap = np.array(Image.open(penn_example_fmap_path))
  974. washu_example_medic_path = data_path / "WashU_medic.png"
  975. washu_example_medic = equalize_hist(np.array(Image.open(washu_example_medic_path)))
  976. washu_example_topup_path = data_path / "WashU_topup.png"
  977. washu_example_topup = equalize_hist(np.array(Image.open(washu_example_topup_path)))
  978. washu_example_fmap_path = data_path / "WashU_fmap.png"
  979. washu_example_fmap = np.array(Image.open(washu_example_fmap_path))
  980. f = plt.figure(figsize=(180 * MM_TO_INCHES, 140 * MM_TO_INCHES), layout="constrained")
  981. gs = GridSpec(
  982. 4,
  983. 3,
  984. left=0.03,
  985. right=0.97,
  986. bottom=0.025,
  987. top=0.96,
  988. wspace=0.15,
  989. hspace=0.125,
  990. height_ratios=[7, 1, 7, 7],
  991. )
  992. gs_cbar = GridSpecFromSubplotSpec(
  993. 3,
  994. 3,
  995. wspace=0,
  996. hspace=0,
  997. width_ratios=[1, 50, 1],
  998. height_ratios=[11, 5, 11],
  999. subplot_spec=gs[1, :],
  1000. )
  1001. ax_WashU_fmap = f.add_subplot(gs[0, 0])
  1002. ax_WashU_medic = f.add_subplot(gs[2, 0])
  1003. ax_WashU_topup = f.add_subplot(gs[3, 0])
  1004. ax_UMinn_fmap = f.add_subplot(gs[0, 1])
  1005. ax_UMinn_medic = f.add_subplot(gs[2, 1])
  1006. ax_UMinn_topup = f.add_subplot(gs[3, 1])
  1007. ax_Penn_fmap = f.add_subplot(gs[0, 2])
  1008. ax_Penn_medic = f.add_subplot(gs[2, 2])
  1009. ax_Penn_topup = f.add_subplot(gs[3, 2])
  1010. cbar_ax = f.add_subplot(gs_cbar[1, 1])
  1011. pl = cbar_ax.imshow(np.array([[-50, 50], [50, -50]]), vmin=-50, vmax=50, aspect="auto", cmap="icefire")
  1012. cbar = f.colorbar(
  1013. pl,
  1014. cax=cbar_ax,
  1015. location="top",
  1016. orientation="horizontal",
  1017. ticks=[-50, -25, 0, 25, 50],
  1018. )
  1019. # create axis for colorbar
  1020. cbar.ax.set_xlabel("Field map Difference (Hz)", labelpad=2)
  1021. alt_vmin, alt_vmax = hz_limits_to_mm(-50, 50)
  1022. cax = cbar.ax.twiny()
  1023. cbar.ax.xaxis.set_ticks_position("top")
  1024. cax.xaxis.set_ticks_position("bottom")
  1025. cax.set_xlim(alt_vmin, alt_vmax)
  1026. cbar.ax.invert_xaxis()
  1027. cax.invert_xaxis()
  1028. cax.xaxis.set_label_position("bottom")
  1029. cax.set_xlabel("Displacement difference (mm)", labelpad=1)
  1030. # plot images
  1031. ax_WashU_fmap.imshow(washu_example_fmap)
  1032. ax_WashU_fmap.set_title(r"$\bf{a}$ WashU Data", pad=6, loc="left")
  1033. ax_WashU_fmap.set_xticks([])
  1034. ax_WashU_fmap.set_yticks([])
  1035. ax_WashU_fmap.set_ylabel("Field Map Difference (MEDIC - TOPUP)", labelpad=4)
  1036. ax_WashU_medic.imshow(washu_example_medic)
  1037. ax_WashU_medic.set_xticks([])
  1038. ax_WashU_medic.set_yticks([])
  1039. ax_WashU_medic.set_ylabel("MEDIC", labelpad=4)
  1040. draw_arrow(
  1041. ax_WashU_medic,
  1042. data_to_ax(ax_WashU_medic, (1683, 1858)),
  1043. data_to_ax(ax_WashU_medic, (1457, 1691)),
  1044. )
  1045. draw_arrow(
  1046. ax_WashU_medic,
  1047. data_to_ax(ax_WashU_medic, (2045, 1620)),
  1048. data_to_ax(ax_WashU_medic, (1829, 1474)),
  1049. )
  1050. ax_WashU_topup.imshow(washu_example_topup)
  1051. ax_WashU_topup.set_xticks([])
  1052. ax_WashU_topup.set_yticks([])
  1053. ax_WashU_topup.set_ylabel("TOPUP", labelpad=4)
  1054. draw_arrow(
  1055. ax_WashU_topup,
  1056. data_to_ax(ax_WashU_topup, (1683, 1858)),
  1057. data_to_ax(ax_WashU_topup, (1457, 1691)),
  1058. )
  1059. draw_arrow(
  1060. ax_WashU_topup,
  1061. data_to_ax(ax_WashU_topup, (2045, 1620)),
  1062. data_to_ax(ax_WashU_topup, (1829, 1474)),
  1063. )
  1064. ax_UMinn_fmap.imshow(minn_example_fmap)
  1065. ax_UMinn_fmap.set_title(r"$\bf{b}$ UMinn Data", pad=6, loc="left")
  1066. ax_UMinn_fmap.set_xticks([])
  1067. ax_UMinn_fmap.set_yticks([])
  1068. ax_UMinn_medic.imshow(minn_example_medic)
  1069. draw_arrow(
  1070. ax_UMinn_medic,
  1071. data_to_ax(ax_UMinn_medic, (1129, 622)),
  1072. data_to_ax(ax_UMinn_medic, (898, 473)),
  1073. )
  1074. ax_UMinn_medic.set_xticks([])
  1075. ax_UMinn_medic.set_yticks([])
  1076. ax_UMinn_topup.imshow(minn_example_topup)
  1077. ax_UMinn_topup.set_xticks([])
  1078. ax_UMinn_topup.set_yticks([])
  1079. draw_arrow(
  1080. ax_UMinn_topup,
  1081. data_to_ax(ax_UMinn_topup, (1129, 622)),
  1082. data_to_ax(ax_UMinn_topup, (898, 473)),
  1083. )
  1084. ax_Penn_fmap.imshow(penn_example_fmap)
  1085. ax_Penn_fmap.set_title(r"$\bf{c}$ Penn Data", pad=6, loc="left")
  1086. ax_Penn_fmap.set_xticks([])
  1087. ax_Penn_fmap.set_yticks([])
  1088. ax_Penn_medic.imshow(penn_example_medic)
  1089. ax_Penn_medic.set_xticks([])
  1090. ax_Penn_medic.set_yticks([])
  1091. draw_arrow(ax_Penn_medic, data_to_ax(ax_Penn_medic, (817, 611)), data_to_ax(ax_Penn_medic, (940, 414)))
  1092. draw_arrow(
  1093. ax_Penn_medic,
  1094. data_to_ax(ax_Penn_medic, (1202, 390)),
  1095. data_to_ax(ax_Penn_medic, (1266, 164)),
  1096. )
  1097. ax_Penn_topup.imshow(penn_example_topup)
  1098. ax_Penn_topup.set_xticks([])
  1099. ax_Penn_topup.set_yticks([])
  1100. draw_arrow(ax_Penn_topup, data_to_ax(ax_Penn_topup, (817, 611)), data_to_ax(ax_Penn_topup, (940, 414)))
  1101. draw_arrow(
  1102. ax_Penn_topup,
  1103. data_to_ax(ax_Penn_topup, (1202, 390)),
  1104. data_to_ax(ax_Penn_topup, (1266, 164)),
  1105. )
  1106. # save figure
  1107. f.savefig(FIGURES_DIR / "fieldmap_comparison.png", dpi=300)
  1108. current_dir = os.getcwd()
  1109. os.chdir(FIGURES_DIR)
  1110. Path("figure4.png").unlink(missing_ok=True)
  1111. Path("figure4.png").symlink_to("fieldmap_comparison.png")
  1112. os.chdir(current_dir)
  1113. sns.set_theme(**GLOBAL_SETTINGS)
  1114. # figure 5
  1115. def spotlight_comparison(data):
  1116. mpl.rcParams["axes.titlesize"] = 7
  1117. mpl.rcParams["axes.labelsize"] = 7
  1118. # load t1 and t2 t stat maps
  1119. t1_tstat = nib.load(Path(data) / "local_corr_t1_tstat.nii.gz").get_fdata().squeeze()
  1120. t2_tstat = nib.load(Path(data) / "local_corr_t2_tstat.nii.gz").get_fdata().squeeze()
  1121. t1_atlas_exemplar = (
  1122. nib.load(AA_DATA_DIR / "sub-20008" / "T1" / "atlas" / "sub-20008_T1w_debias_avg_on_MNI152_T1_2mm.nii.gz")
  1123. .get_fdata()
  1124. .squeeze()
  1125. )
  1126. t2_atlas_exemplar = (
  1127. nib.load(AA_DATA_DIR / "sub-20008" / "T1" / "atlas" / "sub-20008_T2w_debias_avg_on_MNI152_T1_2mm.nii.gz")
  1128. .get_fdata()
  1129. .squeeze()
  1130. )
  1131. # choose slices to iterate over
  1132. slices = np.linspace(16, t1_tstat.shape[2] - 20, 9).astype(int)[::-1]
  1133. # create figure
  1134. f = plt.figure(figsize=(180 * MM_TO_INCHES, 100 * MM_TO_INCHES), layout="constrained")
  1135. # create gridspec
  1136. gs = GridSpec(3, 3, left=0, right=0.975, bottom=0.025, top=0.95, wspace=0.02, hspace=0.02)
  1137. # create subfigures
  1138. subfigs = f.subfigures(1, 3, width_ratios=[1, 5, 5], wspace=0.03)
  1139. cgs = GridSpec(1, 1, left=0.52, right=0.57, bottom=0.073, top=0.89)
  1140. cbar_ax = subfigs[0].add_subplot(cgs[:, :])
  1141. # create axes for each subfigure
  1142. axes_list1 = []
  1143. for i in range(3):
  1144. for j in range(3):
  1145. axes_list1.append(subfigs[1].add_subplot(gs[i, j]))
  1146. axes_list2 = []
  1147. for i in range(3):
  1148. for j in range(3):
  1149. axes_list2.append(subfigs[2].add_subplot(gs[i, j]))
  1150. # create new cmap
  1151. new_cmap = sns.diverging_palette(210, 30, l=70, center="dark", as_cmap=True)
  1152. # plot slices, iterate over list
  1153. for i, s in enumerate(slices):
  1154. ax1 = axes_list1[i]
  1155. ax1.imshow(t1_atlas_exemplar[..., s].T, cmap="gray", origin="lower")
  1156. a = ax1.imshow(t1_tstat[..., s].T, cmap=new_cmap, vmin=-10, vmax=10, origin="lower", alpha=0.75)
  1157. ax1.set_xticks([])
  1158. ax1.set_yticks([])
  1159. if i == 0:
  1160. source_plot = a
  1161. ax2 = axes_list2[i]
  1162. ax2.imshow(t2_atlas_exemplar[..., s].T, cmap="gray", origin="lower")
  1163. ax2.imshow(t2_tstat[..., s].T, cmap=new_cmap, vmin=-10, vmax=10, origin="lower", alpha=0.75)
  1164. ax2.set_xticks([])
  1165. ax2.set_yticks([])
  1166. axes_list1[0].set_title(r"$\bf{a}$ T1w alignment: Spotlight analysis", pad=4, loc="left")
  1167. axes_list2[0].set_title(r"$\bf{b}$ T2w alignment: Spotlight analysis", pad=4, loc="left")
  1168. # create axis for colorbar
  1169. cbar = subfigs[0].colorbar(
  1170. source_plot,
  1171. cax=cbar_ax,
  1172. location="left",
  1173. orientation="vertical",
  1174. )
  1175. cbar.ax.yaxis.set_label_position("right")
  1176. cbar.ax.set_ylabel("t-statistic", labelpad=4)
  1177. cbar.ax.text(
  1178. 0.5,
  1179. 1.05,
  1180. "MEDIC > TOPUP",
  1181. ha="center",
  1182. va="center",
  1183. fontsize=6,
  1184. transform=cbar.ax.transAxes,
  1185. )
  1186. cbar.ax.text(
  1187. 0.5,
  1188. -0.05,
  1189. "TOPUP > MEDIC",
  1190. ha="center",
  1191. va="center",
  1192. fontsize=6,
  1193. transform=cbar.ax.transAxes,
  1194. )
  1195. f.savefig(FIGURES_DIR / "spotlight_comparison.png", dpi=300)
  1196. current_dir = os.getcwd()
  1197. os.chdir(FIGURES_DIR)
  1198. Path("figure5.png").unlink(missing_ok=True)
  1199. Path("figure5.png").symlink_to("spotlight_comparison.png")
  1200. os.chdir(current_dir)
  1201. sns.set_theme(**GLOBAL_SETTINGS)
  1202. # figure 6
  1203. def alignment_metrics(data):
  1204. # plot stats
  1205. data = pd.read_csv(data)
  1206. # create figures
  1207. settings = GLOBAL_SETTINGS.copy()
  1208. settings["style"] = "darkgrid"
  1209. sns.set_theme(**settings)
  1210. mpl.rcParams["axes.labelsize"] = 6
  1211. mpl.rcParams["axes.labelpad"] = 2
  1212. mpl.rcParams["xtick.labelsize"] = 5
  1213. mpl.rcParams["xtick.major.pad"] = 2
  1214. mpl.rcParams["ytick.labelsize"] = 5
  1215. mpl.rcParams["ytick.major.pad"] = 2
  1216. fig = plt.figure(figsize=(180 * MM_TO_INCHES, 90 * MM_TO_INCHES), layout="constrained")
  1217. subfigs = fig.subfigures(1, 2, width_ratios=[2, 1], wspace=0.05)
  1218. subfigs2 = subfigs[0].subfigures(2, 1, hspace=0.05, height_ratios=[1, 3])
  1219. fig_local = subfigs2[0]
  1220. fig_local.suptitle(r"$\bf{a}$ Local Metrics", fontsize=7, ha="left", x=0.02, weight="normal")
  1221. axes_local = fig_local.subplots(1, 2)
  1222. fig_global = subfigs2[1]
  1223. fig_global.suptitle(r"$\bf{b}$ Global Metrics", fontsize=7, ha="left", x=0.02, weight="normal")
  1224. axes_global = fig_global.subplots(3, 2)
  1225. fig_roc = subfigs[1]
  1226. fig_roc.suptitle(r"$\bf{c}$ Segmentation Metrics", fontsize=7, ha="left", x=0.02, weight="normal")
  1227. axes_roc = fig_roc.subplots(4, 1)
  1228. # create list of metrics
  1229. metrics = [
  1230. "local_corr_mean_t1",
  1231. "local_corr_mean_t2",
  1232. "corr_t1",
  1233. "corr_t2",
  1234. "grad_corr_t1",
  1235. "grad_corr_t2",
  1236. "nmi_t1",
  1237. "nmi_t2",
  1238. "roc_gw",
  1239. "roc_ie",
  1240. "roc_vw",
  1241. "roc_cb_ie",
  1242. ]
  1243. # create list of titles
  1244. titles = [
  1245. "T1w R$^2$ Spotlight",
  1246. "T2w R$^2$ Spotlight",
  1247. "T1w R$^2$",
  1248. "T2w R$^2$",
  1249. "T1w Grad. Correlation",
  1250. "T2w Grad. Correlation",
  1251. "T1w NMI",
  1252. "T2w NMI",
  1253. "Gray/White Matter AUC",
  1254. "Brain/Exterior AUC",
  1255. "Ventricles/White Matter AUC",
  1256. "Cerebellum/Exterior AUC",
  1257. ]
  1258. formatted_titles = [
  1259. "T1w R$^2$\nSpotlight",
  1260. "T2w R$^2$\nSpotlight",
  1261. "T1w R$^2$",
  1262. "T2w R$^2$",
  1263. "T1w Grad.\nCorrelation",
  1264. "T2w Grad.\nCorrelation",
  1265. "T1w NMI",
  1266. "T2w NMI",
  1267. "Gray/White\nMatter AUC",
  1268. "Brain/Exterior\nAUC",
  1269. "Ventricles/White\nMatter AUC",
  1270. "Cerebellum/Exterior\nAUC",
  1271. ]
  1272. # create list of axes for plotting
  1273. axes_list = [
  1274. axes_local[0],
  1275. axes_local[1],
  1276. axes_global[0][0],
  1277. axes_global[0][1],
  1278. axes_global[1][0],
  1279. axes_global[1][1],
  1280. axes_global[2][0],
  1281. axes_global[2][1],
  1282. axes_roc[0],
  1283. axes_roc[1],
  1284. axes_roc[2],
  1285. axes_roc[3],
  1286. ]
  1287. # plot box plots
  1288. for m, t, a in zip(metrics, formatted_titles, axes_list):
  1289. plot_box_plot(data, m, t, a)
  1290. # print ttest results
  1291. table_str = "| Metric | MEDIC | TOPUP | t-statistic | p-value | df |\n"
  1292. table_str += "| ------ | ----- | ----- | ----------- | ------- | -- |\n"
  1293. for m, t in zip(metrics, titles):
  1294. medic_data = data[f"{m}_medic"]
  1295. topup_data = data[f"{m}_topup"]
  1296. res = ttest_rel(medic_data, topup_data)
  1297. # round stats
  1298. medic_mean = np.round(medic_data.mean(), 3)
  1299. medic_std = np.round(medic_data.std(), 3)
  1300. topup_mean = np.round(topup_data.mean(), 3)
  1301. topup_std = np.round(topup_data.std(), 3)
  1302. p_value = np.round(res.pvalue, 3)
  1303. p_value = p_value if p_value >= 0.001 else "<0.001"
  1304. t_stat = np.round(res.statistic, 3)
  1305. print(f"{t}:")
  1306. print(f"MEDIC={medic_mean} ({medic_std}); " f"TOPUP={topup_mean} ({topup_std}); " f"p={p_value}; t={t_stat}\n")
  1307. table_str += f"| {t} | {medic_mean} ({medic_std}) | {topup_mean} ({topup_std}) |"
  1308. table_str += f" {t_stat} | {p_value} | {data.shape[0] - 1} |\n"
  1309. print(table_str)
  1310. # save figure
  1311. fig.savefig(FIGURES_DIR / "alignment_metrics.png", dpi=300)
  1312. current_dir = os.getcwd()
  1313. os.chdir(FIGURES_DIR)
  1314. Path("figure6.png").unlink(missing_ok=True)
  1315. Path("figure6.png").symlink_to("alignment_metrics.png")
  1316. os.chdir(current_dir)
  1317. sns.set_theme(**GLOBAL_SETTINGS)
  1318. # figure 7
  1319. def resp_analysis(data):
  1320. settings = GLOBAL_SETTINGS.copy()
  1321. settings["style"] = "darkgrid"
  1322. sns.set_theme(**settings)
  1323. mpl.rcParams["axes.labelsize"] = 6
  1324. mpl.rcParams["axes.labelpad"] = 2
  1325. mpl.rcParams["xtick.labelsize"] = 6
  1326. mpl.rcParams["xtick.major.pad"] = 2
  1327. mpl.rcParams["ytick.labelsize"] = 6
  1328. mpl.rcParams["ytick.major.pad"] = 2
  1329. mpl.rcParams["legend.fontsize"] = 6
  1330. mpl.rcParams["legend.title_fontsize"] = 6
  1331. # load data
  1332. ps_csv = sorted(Path(data).glob("power_spectra_run_*.csv"))
  1333. r_csv = sorted(Path(data).glob("resp_data_run_*.csv"))
  1334. power_spectra = [pd.read_csv(f).set_index("Frequency (Hz)") for f in ps_csv]
  1335. resp_data = [pd.read_csv(f).set_index("VOLUME") for f in r_csv]
  1336. # create figure
  1337. f = plt.figure(figsize=(180 * MM_TO_INCHES, 140 * MM_TO_INCHES), layout="constrained")
  1338. n_rows = len(power_spectra)
  1339. gs = [
  1340. GridSpec(
  1341. 2,
  1342. 2,
  1343. left=0.075,
  1344. right=0.975,
  1345. bottom=0.667 + 0.05,
  1346. top=1.000 - 0.04,
  1347. wspace=0.2,
  1348. hspace=0.2,
  1349. ),
  1350. GridSpec(
  1351. 2,
  1352. 2,
  1353. left=0.075,
  1354. right=0.975,
  1355. bottom=0.333 + 0.05,
  1356. top=0.667 - 0.04,
  1357. wspace=0.2,
  1358. hspace=0.2,
  1359. ),
  1360. GridSpec(
  1361. 2,
  1362. 2,
  1363. left=0.075,
  1364. right=0.975,
  1365. bottom=0.000 + 0.05,
  1366. top=0.333 - 0.04,
  1367. wspace=0.2,
  1368. hspace=0.2,
  1369. ),
  1370. ]
  1371. pastel = sns.color_palette("pastel")
  1372. palette = [pastel[2], pastel[4]]
  1373. ypos = [1.000 - 0.015, 0.667 - 0.015, 0.333 - 0.015]
  1374. for r, l in zip(range(n_rows), ["a", "b", "c"]):
  1375. ps = power_spectra[r]
  1376. rd = resp_data[r]
  1377. if r == 0:
  1378. f.text(
  1379. 0.05,
  1380. ypos[r],
  1381. r"$\bf{a}$ " + f"Spectral Power Density",
  1382. ha="left",
  1383. va="center",
  1384. fontsize=7,
  1385. )
  1386. f.text(
  1387. 0.55,
  1388. ypos[r],
  1389. r"$\bf{b}$ " + f"Respiratory Signal",
  1390. ha="left",
  1391. va="center",
  1392. fontsize=7,
  1393. )
  1394. # plot power spectra
  1395. ax1 = f.add_subplot(gs[r][0, 0])
  1396. sns.lineplot(data=ps["resp_signal"], linewidth=0.6, color=pastel[3], ax=ax1)
  1397. ax1.set_ylim(0, 60)
  1398. ax1.set_xlabel("")
  1399. ax1.set_ylabel("Respiratory Belt\nPower Density", labelpad=2)
  1400. ax1.set_xticklabels([])
  1401. ax1.tick_params(axis="x")
  1402. ax1.tick_params(axis="y")
  1403. ax1.set_title(f"Run {r + 1}")
  1404. ax2 = f.add_subplot(gs[r][1, 0])
  1405. sns.lineplot(
  1406. data=ps[["fmap_signal", "fmap_signal_filtered"]],
  1407. dashes=False,
  1408. linewidth=0.6,
  1409. palette=palette,
  1410. ax=ax2,
  1411. )
  1412. ax2.set_ylim(0, 60)
  1413. ax2.legend(["Unfiltered", "Filtered"], loc="upper center")
  1414. ax2.set_ylabel("MEDIC Field Map\nPower Density", labelpad=2)
  1415. ax2.set_xlabel("Frequency (Hz)", labelpad=2)
  1416. ax2.tick_params(axis="x")
  1417. ax2.tick_params(axis="y")
  1418. # plot resp signals
  1419. tr = 1.761
  1420. rd.index = rd.index * tr
  1421. corr = np.corrcoef(rd["resp_signal"], rd["fmap_signal"])[0, 1]
  1422. ax3 = f.add_subplot(gs[r][0, 1])
  1423. sns.lineplot(data=rd["resp_signal"], linewidth=0.6, color=pastel[3], ax=ax3)
  1424. ax3.set_xlabel("")
  1425. ax3.set_ylabel("Signal from\nRespiratory Belt", labelpad=2)
  1426. ax3.set_xticklabels([])
  1427. ax3.tick_params(axis="x")
  1428. ax3.tick_params(axis="y")
  1429. ax3.set_title(f"R = {np.round(corr, 3)}")
  1430. ax3.set_ylim(-3, 3)
  1431. ax4 = f.add_subplot(gs[r][1, 1])
  1432. sns.lineplot(data=rd["fmap_signal"], linewidth=0.6, color=palette[1], ax=ax4)
  1433. ax4.set_xlabel("Time (seconds)", labelpad=2)
  1434. ax4.set_ylabel("Signal from\nMEDIC Field Map", labelpad=2)
  1435. ax4.tick_params(axis="x")
  1436. ax4.tick_params(axis="y")
  1437. ax4.set_ylim(-3, 3)
  1438. f.savefig(FIGURES_DIR / "resp_analysis.png", dpi=300)
  1439. current_dir = os.getcwd()
  1440. os.chdir(FIGURES_DIR)
  1441. Path("figure7.png").unlink(missing_ok=True)
  1442. Path("figure7.png").symlink_to("resp_analysis.png")
  1443. os.chdir(current_dir)
  1444. sns.set_theme(**GLOBAL_SETTINGS)
  1445. # figure 10
  1446. def tsnr_comparision(data):
  1447. # load tsnralignment_metrics
  1448. tsnr_table = pd.read_csv(data)
  1449. settings = GLOBAL_SETTINGS.copy()
  1450. settings["style"] = "darkgrid"
  1451. sns.set_theme(**settings)
  1452. mpl.rcParams["axes.labelsize"] = 7
  1453. mpl.rcParams["axes.labelpad"] = 2
  1454. mpl.rcParams["xtick.labelsize"] = 7
  1455. mpl.rcParams["xtick.major.pad"] = 2
  1456. mpl.rcParams["ytick.labelsize"] = 7
  1457. mpl.rcParams["ytick.major.pad"] = 2
  1458. f = plt.figure(figsize=(90 * MM_TO_INCHES, 45 * MM_TO_INCHES), layout="constrained")
  1459. ax = f.add_subplot(1, 1, 1)
  1460. mpl.rcParams["xtick.labelsize"] = 6
  1461. mpl.rcParams["ytick.labelsize"] = 6
  1462. plot_box_plot(tsnr_table, "mean_tsnr_masked", "tSNR", ax)
  1463. ax.set_xlim([0, 160])
  1464. m = "mean_tsnr_masked"
  1465. t = "tSNR"
  1466. medic_data = tsnr_table[f"{m}_medic"]
  1467. topup_data = tsnr_table[f"{m}_topup"]
  1468. res = ttest_rel(medic_data, topup_data)
  1469. # round stats
  1470. medic_mean = np.round(medic_data.mean(), 3)
  1471. medic_std = np.round(medic_data.std(), 3)
  1472. topup_mean = np.round(topup_data.mean(), 3)
  1473. topup_std = np.round(topup_data.std(), 3)
  1474. p_value = np.round(res.pvalue, 3)
  1475. p_value = p_value if p_value >= 0.001 else "<0.001"
  1476. t_stat = np.round(res.statistic, 3)
  1477. print(f"{t}:")
  1478. print(f"MEDIC={medic_mean} ({medic_std}); " f"TOPUP={topup_mean} ({topup_std}); " f"p={p_value}; t={t_stat}\n")
  1479. f.savefig(FIGURES_DIR / "tsnr.png", dpi=300)
  1480. current_dir = os.getcwd()
  1481. os.chdir(FIGURES_DIR)
  1482. Path("figure10.png").unlink(missing_ok=True)
  1483. Path("figure10.png").symlink_to("tsnr.png")
  1484. os.chdir(current_dir)
  1485. sns.set_theme(**GLOBAL_SETTINGS)
  1486. # figure 100
  1487. def head_position_videos(data):
  1488. settings = GLOBAL_SETTINGS.copy()
  1489. settings["rc"].update(
  1490. {
  1491. "axes.facecolor": "black",
  1492. "figure.facecolor": "black",
  1493. "axes.labelcolor": "white",
  1494. "axes.titlecolor": "white",
  1495. "text.color": "white",
  1496. "xtick.color": "white",
  1497. "ytick.color": "white",
  1498. }
  1499. )
  1500. sns.set_theme(**settings)
  1501. # load field map files
  1502. medic_fieldmaps = Path(data) / "fieldmaps" / "medic_aligned"
  1503. transient_head_position_run_idx = [7, 8, 9, 10, 11, 12, 13]
  1504. # load transient field map runs
  1505. transient_fieldmaps = []
  1506. for idx in transient_head_position_run_idx:
  1507. run = idx + 1
  1508. transient_fieldmaps.append(nib.load(medic_fieldmaps / f"run{run:02d}" / "fmap.nii.gz"))
  1509. labels = [
  1510. "Neutral",
  1511. "+Z Rotation",
  1512. "-Z Rotation",
  1513. "+X Rotation",
  1514. "-X Rotation",
  1515. "+Y Rotation",
  1516. "-Y Rotation",
  1517. "Neutral to +Z Rotation",
  1518. "Neutral to -Z Rotation",
  1519. "Neutral to +X Rotation",
  1520. "Neutral to -X Rotation",
  1521. "Neutral to +Y Rotation",
  1522. "Neutral to -Y Rotation",
  1523. "Neutral to -Z Translation",
  1524. "-Z Translation",
  1525. ]
  1526. # get labels
  1527. labels = [labels[i] for i in transient_head_position_run_idx]
  1528. # replace space with underscores
  1529. labels = [label.replace(" ", "_") for label in labels]
  1530. # make transients output directory
  1531. transients_out = Path(FIGURES_DIR) / "videos" / "transients"
  1532. transients_out.mkdir(parents=True, exist_ok=True)
  1533. # load motion parameters
  1534. motion_params = []
  1535. for idx in transient_head_position_run_idx:
  1536. run = idx + 1
  1537. motion_params.append(
  1538. np.loadtxt(Path(data) / "framewise_align" / "func" / f"run{run:02d}" / f"run{run:02d}.par")
  1539. )
  1540. motion_params[-1][:, :3] = np.rad2deg(motion_params[-1][:, :3])
  1541. # render transient field map videos
  1542. def set_moco_label(motion_params):
  1543. def set_figure_labels(fig, frame_num):
  1544. # set label on figure
  1545. fig.text(
  1546. 0.5,
  1547. 0.7,
  1548. f"Frame {frame_num}"
  1549. f"\nMotion Parameters:"
  1550. f"\nrot-x: {motion_params[frame_num, 0]:.2f} deg"
  1551. f"\nrot-y: {motion_params[frame_num, 1]:.2f} deg"
  1552. f"\nrot-z: {motion_params[frame_num, 2]:.2f} deg"
  1553. f"\ntx: {motion_params[frame_num, 3]:.2f} mm"
  1554. f"\nty: {motion_params[frame_num, 4]:.2f} mm"
  1555. f"\ntz: {motion_params[frame_num, 5]:.2f} mm",
  1556. ha="center",
  1557. )
  1558. # return figure
  1559. return fig
  1560. # return function
  1561. return set_figure_labels
  1562. gs0 = GridSpec(1, 1, left=0.05, right=0.1, bottom=0.05, top=0.95)
  1563. gs1 = GridSpec(
  1564. 1,
  1565. 3,
  1566. left=0.125,
  1567. right=0.95,
  1568. bottom=0.05,
  1569. top=0.95,
  1570. hspace=0.025,
  1571. wspace=0.025,
  1572. width_ratios=[72, 110, 110],
  1573. )
  1574. def fig_callback():
  1575. fig = plt.figure(figsize=(10, 6), layout="constrained")
  1576. axes_list = [fig.add_subplot(gs1[0, i]) for i in range(3)]
  1577. cbar_ax = fig.add_subplot(gs0[:, :])
  1578. cbar_ax.axis("off")
  1579. return fig, axes_list, cbar_ax
  1580. for fmap, moco, label in zip(transient_fieldmaps, motion_params, labels):
  1581. print(f"Processing {label}")
  1582. render_dynamic_figure(
  1583. str(transients_out / f"{label}.mp4"),
  1584. [fmap],
  1585. colorbar=True,
  1586. colorbar_aspect=60,
  1587. colorbar_pad=0,
  1588. colorbar_labelpad=-5,
  1589. colorbar_alt_range=True,
  1590. colorbar_alt_labelpad=0,
  1591. fraction=0.3,
  1592. vmin=-100,
  1593. vmax=150,
  1594. colormaps="icefire",
  1595. figure_fx=set_moco_label(moco),
  1596. fig_callback=fig_callback,
  1597. text_color="white",
  1598. )
  1599. sns.set_theme(**GLOBAL_SETTINGS)
  1600. def main():
  1601. parser = argparse.ArgumentParser(description="script for generating paper figures")
  1602. parser.add_argument("--figures", nargs="+", type=int, help="figures to generate, if not supplied will plot all")
  1603. parser.add_argument("--figure_1_data", default=FIGURE1_DATA, help="path to figure 1 data")
  1604. parser.add_argument("--figure_2_data", default=FIGURE2_DATA, help="path to figure 2 data")
  1605. parser.add_argument("--figure_3_data", default=FIGURE3_DATA, help="path to figure 3 data")
  1606. parser.add_argument("--figure_4_data", default=FIGURE4_DATA, help="path to figure 4 data")
  1607. parser.add_argument("--figure_5_data", default=FIGURE5_DATA, help="path to figure 5 data")
  1608. parser.add_argument("--figure_6_data", default=FIGURE6_DATA, help="path to figure 6 data")
  1609. parser.add_argument("--figure_7_data", default=FIGURE7_DATA, help="path to figure 7 data")
  1610. parser.add_argument("--figure_10_data", default=FIGURE10_DATA, help="path to figure 10 data")
  1611. parser.add_argument("--figure_100_data", default=FIGURE100_DATA, help="path to figure 100 data")
  1612. # get arguments
  1613. args = parser.parse_args()
  1614. if args.figures is None or 1 in args.figures:
  1615. head_position_fieldmap(args.figure_1_data)
  1616. if args.figures is None or 2 in args.figures:
  1617. head_concatenation(args.figure_2_data)
  1618. if args.figures is None or 3 in args.figures:
  1619. group_template_comparison(args.figure_3_data)
  1620. if args.figures is None or 4 in args.figures:
  1621. fmap_comparison(args.figure_4_data)
  1622. if args.figures is None or 5 in args.figures:
  1623. spotlight_comparison(args.figure_5_data)
  1624. if args.figures is None or 6 in args.figures:
  1625. alignment_metrics(args.figure_6_data)
  1626. if args.figures is None or 7 in args.figures:
  1627. resp_analysis(args.figure_7_data)
  1628. if args.figures is None or 10 in args.figures:
  1629. tsnr_comparision(args.figure_10_data)
  1630. if args.figures is not None and 100 in args.figures:
  1631. head_position_videos(args.figure_100_data)
  1632. plt.show()

paper_figures.py at commit cc2ea27, under MIT · at the source

Overview

Authors: Andrew N Van1,2, David F Montez2,3, Timothy O Laumann3, Philip N Cho2, Vahdeta Suljic2, Thomas Madison4,5, Noah J Baden2, Nadeshka Ramirez-Perez2, Kristen M Scheidter2, Julia S Monk2, Forrest I Whiting2, Babatunde Adeyemo2, Roselyne J Chauvin2, Samuel R Krimmel2, Athanasia Metoki2, Aishwarya Rajesh6, Jarod L Roland7, Taylor Salo8, Anxu Wang2,9, Kimberly B Weldon5
and 13 other authorsAristeidis Sotiras6,10, Joshua S Shimony6, Benjamin P Kay2, Steven M Nelson5,11, Brenden Tervo-Clemmens5,12, Scott A Marek6, Luca Vizioli13, Essa Yacoub13, Theodore D Satterthwaite8, Evan M Gordon6, Damien A Fair4,5,11, M Dylan Tisdall14, Nico UF Dosenbach1,2,6,15
15 affiliations
  1. Department of Biomedical Engineering, Washington University in St. Louis, St. Louis, MO, United States
  2. Department of Neurology, Washington University School of Medicine, St. Louis, MO, United States
  3. Department of Psychiatry, Washington University School of Medicine, St. Louis, MO, United States
  4. Institute of Child Development, University of Minnesota Medical School, Minneapolis, MN, United States
  5. Masonic Institute for the Developing Brain, University of Minnesota Medical School, Minneapolis, MN, United States
  6. Department of Radiology, Washington University School of Medicine, St. Louis, MO, United States
  7. Department of Neurosurgery, Washington University School of Medicine, St. Louis, MO, United States
  8. Lifespan Informatics and Neuroimaging Center (PennLINC), Perelman School of Medicine, University of Pennsylvania, Philadelphia, PA, United States
  9. Division of Computation and Data Science, Washington University School of Medicine, St. Louis, MO, United States
  10. Institute for Informatics, Data Science & Biostatistics, Washington University School of Medicine, St. Louis, MO, United States
  11. Department of Pediatrics, University of Minnesota Medical School, Minneapolis, MN, United States
  12. Department of Psychiatry & Behavioral Sciences, University of Minnesota Medical School, Minneapolis, MN, United States
  13. Center for Magnetic Resonance Research, University of Minnesota Medical School, Minneapolis, MN, United States
  14. Department of Radiology, Perelman School of Medicine, University of Pennsylvania, Philadelphia, PA, United States
  15. Department of Pediatrics, Washington University School of Medicine, St. Louis, MO, United States
Institutions: Washington University in St. Louis (United States); University of Minnesota (United States); University of Pennsylvania (United States)
Journal: Imaging neuroscience (Cambridge, Mass.), volume 4, article IMAG.a.1262
Dates: received 22 January 2024; accepted 22 April 2026; published online 29 May 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1162/imag.a.1262 · PMID 42232073 · PMCID PMC13224312 · OpenAlex W4389154158
Open access: diamond, a free copy (OpenAlex)
Status: code verified
Categories: fMRI (modality)
Methods: Connectivity, Statistics, fMRI & imaging
Keywords: distortion correction, fMRI, multi-echo
Topic: Advanced MRI Techniques and Applications (Radiology, Nuclear Medicine and Imaging, Medicine), according to OpenAlex
Funding: NIMH NIH HHS (R00 MH129616)
Citations: cited by 1 paper (Europe PMC); 62 references in the paper

Abstract

Functional MRI (fMRI) data are severely distorted by magnetic field (B0) inhomogeneities, which currently must be corrected using separately acquired field map data. However, changes in the head position of a participant across fMRI frames cause changes in the B0 field, preventing accurate correction of geometric distortions. Movement during field map acquisitions corrupts field maps, preventing distortion correction altogether. In this study, we use multi-echo (ME) fMRI data to dynamically sample and correct for magnetic field image distortions caused by head motion. Our distortion correction pipeline, MEDIC (Multi-Echo DIstortion Correction), leverages magnetic field inhomogeneity information found in the difference between echoes and uses it to correct for distortion on a frame-by-frame basis. Here, we demonstrate that MEDIC’s frame-wise distortion correction decreases the impact of head motion on resting-state functional connectivity (RSFC) maps and improves alignment to anatomy when compared with the prior gold standard approach (i.e., FSL TOPUP). Enhanced frame-wise distortion correction with MEDIC, without the requirement for field map collection, furthers the benefit of cutting-edge multi-echo fMRI imaging over single-echo fMRI.

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

Repositories

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

vanandrew/warpkit

License: other
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 4ae7699d23d5d7404a9cc4386de2bd5d3f0335e7, 6 August 2026
Languages: Python (38), C/C++ (12), Shell (2), C++ (1)
Size: 103 files, 53 scripts
Software Heritage: archived
Found in: the text, “MEDIC is computationally efficient and open sour”
Holds: README, license file, CITATION.cff, environment (Dockerfile, pyproject.toml, uv.lock), tests, continuous integration
Not found: documentation
Tools: NiBabel (20 files), NumPy (15 files), SciPy (3 files), scikit-image (2 files)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
55 files

DosenbachGreene/processing_pipeline

License: MIT
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 8be76287c68caf556ed96ba42d116c02a9b77c66, 20 October 2023
Languages: Python (21), Shell (10), C (2), C/C++ (1)
Size: 228 files, 34 scripts
Software Heritage: not archived
Found in: “Data and Code Availability”
Holds: README, license file, environment (Apptainer, compose.yml, Dockerfile, pyproject.toml, setup.cfg, setup.py), tests
Not found: CITATION.cff, continuous integration, documentation
Tools: FSL (4 files), NumPy (4 files), FreeSurfer (3 files), NiBabel (2 files), PyBIDS (2 files), Connectome Workbench (2 files), HeuDiConv (1 file), Matplotlib (1 file), pandas (1 file), SciPy (1 file), seaborn (1 file)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
36 files

vanandrew/medic_analysis

License: MIT
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: cc2ea27a90085e50762a25820773851310da1e31, 21 January 2024
Languages: MATLAB (555), Python (15), C (8)
Size: 747 files, 578 scripts
Software Heritage: archived
Found in: “Data and Code Availability”
Holds: README, license file, environment (Dockerfile, pyproject.toml, setup.cfg, setup.py)
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: FieldTrip (175 files), Statistics and Machine Learning Toolbox (16 files), SPM (12 files), GIfTI library for MATLAB (10 files), NiBabel (9 files), NumPy (9 files), Connectome Workbench (8 files), FreeSurfer (5 files), SciPy (5 files), pandas (4 files), scikit-image (4 files), FSL (3 files), Matplotlib (3 files), Parallel Computing Toolbox (2 files), PyBIDS (2 files), seaborn (2 files), Image Processing Toolbox (1 file), Optimization Toolbox (1 file), Tools for NIfTI and ANALYZE image (MATLAB) (1 file), Nilearn (1 file), Pillow (1 file), pydicom (1 file)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
580 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:

  • 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 665 scripts, each with its path and the digest of its content;
  • 20 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 implementation for MEDIC is found at https://github.com/vanandrew/warpkit. Code for the processing pipeline is found at https://github.com/DosenbachGreene/processing_pipeline. Code for data analysis and figure generation is found at https://github.com/vanandrew/medic_analysis. Data used in our study are found at https://dosenbachlab.wustl.edu/data.

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

Recorded: type, language, journal, volume, pages, dates, 33 authors, 3 keywords, 1 funder, 62 references.

Cite

This paper

Van, A. N., Montez, D. F., Laumann, T. O., Cho, P. N., Suljic, V., Madison, T., Baden, N. J., Ramirez-Perez, N., Scheidter, K. M., Monk, J. S., Whiting, F. I., Adeyemo, B., Chauvin, R. J., Krimmel, S. R., Metoki, A., Rajesh, A., Roland, J. L., Salo, T., Wang, A., . . . Dosenbach, N. U. (2026). Frame-wise multi-echo distortion correction for superior functional MRI. Imaging neuroscience (Cambridge, Mass.), 4, IMAG.a.1262. https://doi.org/10.1162/imag.a.1262

BibTeX

@article{van2026frame,
author = {Van, Andrew N and Montez, David F and Laumann, Timothy O and Cho, Philip N and Suljic, Vahdeta and Madison, Thomas and Baden, Noah J and Ramirez-Perez, Nadeshka and Scheidter, Kristen M and Monk, Julia S and Whiting, Forrest I and Adeyemo, Babatunde and Chauvin, Roselyne J and Krimmel, Samuel R and Metoki, Athanasia and Rajesh, Aishwarya and Roland, Jarod L and Salo, Taylor and Wang, Anxu and Weldon, Kimberly B and Sotiras, Aristeidis and Shimony, Joshua S and Kay, Benjamin P and Nelson, Steven M and Tervo-Clemmens, Brenden and Marek, Scott A and Vizioli, Luca and Yacoub, Essa and Satterthwaite, Theodore D and Gordon, Evan M and Fair, Damien A and Tisdall, M Dylan and Dosenbach, Nico UF},
title = {{Frame-wise multi-echo distortion correction for superior functional MRI}},
journal = {Imaging neuroscience (Cambridge, Mass.)},
year = {2026},
month = may,
volume = {4},
pages = {IMAG.a.1262},
publisher = {MIT Press},
issn = {2837-6056},
doi = {10.1162/imag.a.1262},
url = {https://doi.org/10.1162/imag.a.1262},
pmid = {42232073},
pmcid = {PMC13224312}
}

RIS

TY - JOUR
AU - Van, Andrew N
AU - Montez, David F
AU - Laumann, Timothy O
AU - Cho, Philip N
AU - Suljic, Vahdeta
AU - Madison, Thomas
AU - Baden, Noah J
AU - Ramirez-Perez, Nadeshka
AU - Scheidter, Kristen M
AU - Monk, Julia S
AU - Whiting, Forrest I
AU - Adeyemo, Babatunde
AU - Chauvin, Roselyne J
AU - Krimmel, Samuel R
AU - Metoki, Athanasia
AU - Rajesh, Aishwarya
AU - Roland, Jarod L
AU - Salo, Taylor
AU - Wang, Anxu
AU - Weldon, Kimberly B
AU - Sotiras, Aristeidis
AU - Shimony, Joshua S
AU - Kay, Benjamin P
AU - Nelson, Steven M
AU - Tervo-Clemmens, Brenden
AU - Marek, Scott A
AU - Vizioli, Luca
AU - Yacoub, Essa
AU - Satterthwaite, Theodore D
AU - Gordon, Evan M
AU - Fair, Damien A
AU - Tisdall, M Dylan
AU - Dosenbach, Nico UF
TI - Frame-wise multi-echo distortion correction for superior functional MRI
T2 - Imaging neuroscience (Cambridge, Mass.)
J2 - Imaging Neurosci (Camb)
PY - 2026
DA - 2026/05/29
VL - 4
SP - IMAG.a.1262
SN - 2837-6056
PB - MIT Press
DO - 10.1162/imag.a.1262
UR - https://doi.org/10.1162/imag.a.1262
LA - en
ER -

CSL-JSON

{
"id": "10.1162/imag.a.1262",
"type": "article-journal",
"title": "Frame-wise multi-echo distortion correction for superior functional MRI",
"container-title": "Imaging neuroscience (Cambridge, Mass.)",
"author": [
{
"family": "Van",
"given": "Andrew N"
},
{
"family": "Montez",
"given": "David F"
},
{
"family": "Laumann",
"given": "Timothy O"
},
{
"family": "Cho",
"given": "Philip N"
},
{
"family": "Suljic",
"given": "Vahdeta"
},
{
"family": "Madison",
"given": "Thomas"
},
{
"family": "Baden",
"given": "Noah J"
},
{
"family": "Ramirez-Perez",
"given": "Nadeshka"
},
{
"family": "Scheidter",
"given": "Kristen M"
},
{
"family": "Monk",
"given": "Julia S"
},
{
"family": "Whiting",
"given": "Forrest I"
},
{
"family": "Adeyemo",
"given": "Babatunde"
},
{
"family": "Chauvin",
"given": "Roselyne J"
},
{
"family": "Krimmel",
"given": "Samuel R"
},
{
"family": "Metoki",
"given": "Athanasia"
},
{
"family": "Rajesh",
"given": "Aishwarya"
},
{
"family": "Roland",
"given": "Jarod L"
},
{
"family": "Salo",
"given": "Taylor"
},
{
"family": "Wang",
"given": "Anxu"
},
{
"family": "Weldon",
"given": "Kimberly B"
},
{
"family": "Sotiras",
"given": "Aristeidis"
},
{
"family": "Shimony",
"given": "Joshua S"
},
{
"family": "Kay",
"given": "Benjamin P"
},
{
"family": "Nelson",
"given": "Steven M"
},
{
"family": "Tervo-Clemmens",
"given": "Brenden"
},
{
"family": "Marek",
"given": "Scott A"
},
{
"family": "Vizioli",
"given": "Luca"
},
{
"family": "Yacoub",
"given": "Essa"
},
{
"family": "Satterthwaite",
"given": "Theodore D"
},
{
"family": "Gordon",
"given": "Evan M"
},
{
"family": "Fair",
"given": "Damien A"
},
{
"family": "Tisdall",
"given": "M Dylan"
},
{
"family": "Dosenbach",
"given": "Nico UF"
}
],
"container-title-short": "Imaging Neurosci (Camb)",
"volume": "4",
"page": "IMAG.a.1262",
"DOI": "10.1162/imag.a.1262",
"PMID": "42232073",
"PMCID": "PMC13224312",
"ISSN": "2837-6056",
"publisher": "MIT Press",
"URL": "https://doi.org/10.1162/imag.a.1262",
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
29
]
]
}
}

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.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: Connectome Workbench, GIfTI library for MATLAB, Tools for NIfTI and ANALYZE image (MATLAB), 12 other tools, fMRI, 7 references
[2] doi:10.1162/imag.a.1222 [code]
Network-based near-scalp personalized brain stimulation targets.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Connectome Workbench, GIfTI library for MATLAB, Tools for NIfTI and ANALYZE image (MATLAB), 11 other tools, 6 references
[3] doi:10.1002/hbm.70621 [code]
Optimising 7T-fMRI for Imaging Regions of Magnetic Susceptibility.
Journal: Human brain mapping
In common: HeuDiConv, Tools for NIfTI and ANALYZE image (MATLAB), FieldTrip, 4 other tools, fMRI, 10 references
[4] doi:10.1038/s41467-026-73668-y [code]
Convergent and divergent brain-cognition development in early adolescence.
Journal: Nature communications
In common: Connectome Workbench, GIfTI library for MATLAB, Tools for NIfTI and ANALYZE image (MATLAB), 13 other tools, fMRI, 3 references
[5] doi:10.1038/s41586-026-10631-3 [code]
A prognostic human brain network for diffuse midline glioma.
Journal: Nature
In common: pydicom, GIfTI library for MATLAB, Tools for NIfTI and ANALYZE image (MATLAB), 15 other tools, 1 reference
[6] 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: Connectome Workbench, GIfTI library for MATLAB, Optimization Toolbox, 15 other tools, 1 reference
[7] doi:10.1162/imag.a.1198 [code]
MEPrep: A robust pipeline for multi-echo fMRI denoising and preprocessing.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: PyBIDS, FreeSurfer, FSL, 8 other tools, fMRI, 8 references
[8] doi:10.1111/ene.70678 [code]
Who Falls After a Stroke? Evidence From a Prospective Stroke Cohort.
Journal: European journal of neurology
In common: pydicom, GIfTI library for MATLAB, Tools for NIfTI and ANALYZE image (MATLAB), 13 other tools, 2 references
[9] doi:10.1162/netn.a.547 [code]
An evaluation of the efficacy of single-echo and multi-echo fMRI denoising strategies.
Journal: Network neuroscience (Cambridge, Mass.)
In common: FreeSurfer, FSL, Nilearn, 6 other tools, fMRI, 10 references
[10] doi:10.1016/j.celrep.2026.117404 [code]
Action and rest tremor map to distinct networks within the primary motor cortex.
Journal: Cell reports
In common: pydicom, GIfTI library for MATLAB, Tools for NIfTI and ANALYZE image (MATLAB), 13 other tools, 1 reference

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.