OSCR

NeuroFM: Toward Precision Neuroimaging with Foundation Models for Individualized Brain Health Estimation

Code ↔ Paper

2 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 2 matches
  1. [1] § Methods › Method 1: Data and preprocessing › Preprocessing ↔ neurofm/io.py, lines 203 to 279 · score 0.54 · FreeSurfer, conformed, resolution, preprocessing, voxel, MRI · Report a mismatch
  2. [2] § Methods › Method 3: Predictor mode ↔ neurofm/inference.py, lines 184 to 224 · score 0.51 · predict brain, ventricular volume, brain volume, raw, brain health, sex · Report a mismatch

Paper

Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 536 lines · 18 KB · CC-BY-NC-SA-4.0 · 1 match

  1. #!/usr/bin/env python3
  2. """
  3. =============================================================================
  4. Austin Dibble
  5. University of Glasgow
  6. 2026
  7. neurofm/io.py
  8. Input resolution, preprocessing, and output writing.
  9. Accepts a single file, directory, or CSV with an 'input' column.
  10. Output modes
  11. ------------
  12. flat All outputs written to a single flat directory. Default.
  13. Note that one subdirectory is created by default: individuals.
  14. This contains all individual-image outputs, separate from the
  15. summary files in the output root.
  16. mirror Output directory tree mirrors the input directory structure.
  17. summary Summary CSV and aggregate latent .npy only - no per-file outputs.
  18. Useful when disk space is a concern or only aggregate results needed.
  19. Individual-level directory by default follows 'flat'.
  20. Caching
  21. -------
  22. Per-file brain_health outputs double as a cache. If an output file already
  23. exists and --overwrite is not set, the file is loaded from disk and included
  24. in the final aggregate rather than re-running inference. Logged at DEBUG level.
  25. =============================================================================
  26. """
  27. from __future__ import annotations
  28. import os
  29. import warnings
  30. from glob import glob
  31. from pathlib import Path
  32. from typing import List, Optional
  33. import nibabel as nib
  34. import numpy as np
  35. import pandas as pd
  36. from loguru import logger
  37. from nibabel.processing import conform as nibabel_conform
  38. from scipy import stats
  39. from .custom_conform import pad_orient_conform
  40. from .model import _BRAIN_HEALTH_INTERNAL, BRAIN_HEALTH_KEYS # noqa: F401
  41. SUPPORTED_EXTENSIONS = (".nii", ".nii.gz")
  42. OUTPUT_MODES = ("flat", "mirror", "summary")
  43. # ---------------------------------------------------------------------------
  44. # Input resolution
  45. # ---------------------------------------------------------------------------
  46. def resolve_inputs(source: str, input_col:str = None) -> List[str]:
  47. """
  48. Resolve a user-provided source to a flat list of NIfTI file paths.
  49. Parameters
  50. ----------
  51. source : str
  52. Path to a single NIfTI file, a directory, or a CSV file with an
  53. 'input' column containing NIfTI paths.
  54. Returns
  55. -------
  56. list[str]
  57. Resolved and validated list of absolute file paths.
  58. """
  59. source = os.path.expanduser(source)
  60. source = Path(source)
  61. if source.name.endswith(".csv"):
  62. paths = _resolve_csv(source)
  63. elif source.is_dir():
  64. paths = _resolve_directory(source)
  65. elif source.is_file():
  66. paths = _resolve_single(source)
  67. else:
  68. raise FileNotFoundError(f"Input not found: {source}")
  69. if not paths:
  70. raise ValueError(f"No supported NIfTI files found in: {source}")
  71. logger.info(f"Resolved {len(paths)} input file(s).")
  72. return paths
  73. def _resolve_single(path) -> List[str]:
  74. """Give us the absolute path to the given input path as an array,
  75. to fit with the expected formatting.
  76. """
  77. path = Path(path)
  78. if not any(path.name.endswith(ext) for ext in SUPPORTED_EXTENSIONS):
  79. raise ValueError(
  80. f"Unsupported file type: {path}. Expected one of {SUPPORTED_EXTENSIONS}."
  81. )
  82. return [Path(os.path.abspath(path))]
  83. def _resolve_directory(directory: str) -> List[str]:
  84. """Get all the relevant .nii.gz files in the directory."""
  85. paths = []
  86. for ext in SUPPORTED_EXTENSIONS:
  87. pattern = os.path.join(directory, "**", f"*{ext}")
  88. paths.extend(glob(pattern, recursive=True))
  89. seen = set()
  90. unique = []
  91. for p in sorted(paths):
  92. if p not in seen:
  93. seen.add(p)
  94. unique.append(Path(os.path.abspath(p)))
  95. return unique
  96. def _resolve_csv(csv_path, input_col:str = "input") -> list:
  97. """Get the paths from the given .csv file. Returns the discovered paths as a list."""
  98. df = pd.read_csv(csv_path)
  99. if input_col not in df.columns:
  100. raise ValueError(
  101. f"CSV must contain an '{input_col}' column. Found columns: {list(df.columns)}"
  102. )
  103. paths = df[input_col].dropna().tolist()
  104. missing = [p for p in paths if not os.path.isfile(p)]
  105. if missing:
  106. logger.warning(
  107. f"{len(missing)} path(s) in CSV do not exist and will be skipped:\n"
  108. + "\n".join(missing[:5])
  109. + ("\n ..." if len(missing) > 5 else "")
  110. )
  111. paths = [p for p in paths if os.path.isfile(p)]
  112. return [Path(os.path.abspath(p)) for p in paths]
  113. # ---------------------------------------------------------------------------
  114. # Preprocessing
  115. # ---------------------------------------------------------------------------
  116. TARGET_SHAPE = (256, 256, 256) # Model input shape is fixed 256^3, so we pad
  117. TARGET_ZOOMS = (1.0, 1.0, 1.0) # We want the MRI in 1mm iso
  118. TARGET_ORIENTATION = "LIA" # all brains should be conformed to LIA, which was the training space
  119. def load_and_preprocess(path: str, custom_preproc_fn = None) -> np.ndarray:
  120. """
  121. Load a NIfTI file, apply orientation correction and resampling,
  122. and return a preprocessed numpy array ready for model input.
  123. Parameters
  124. ----------
  125. path : str
  126. Path to a skull-stripped T1w NIfTI file.
  127. Returns
  128. -------
  129. np.ndarray
  130. Preprocessed volume, shape (1, X, Y, Z, 1) - batch and channel dims
  131. included for direct model input.
  132. """
  133. img = nib.load(path)
  134. return preprocess_nifti(img, custom_preproc_fn = custom_preproc_fn)
  135. def preprocess_nifti(img, custom_preproc_fn=None):
  136. img = _resample(img) # reorients internally if resampling occurs
  137. img = _reorient(img) # no-op if already correct; fallback if resample short-circuited
  138. data = img.get_fdata(dtype=np.float32)
  139. if custom_preproc_fn is not None:
  140. data = custom_preproc_fn(data)
  141. data = _normalize(data)
  142. return data[np.newaxis, ..., np.newaxis]
  143. def _reorient(img: nib.Nifti1Image) -> nib.Nifti1Image:
  144. """Reorient the given nifti image into the correct orientation space, as needed.
  145. Image should already be in 256^3 1mm iso space by this point.
  146. Args:
  147. img (nib.Nifti1Image): Input volume
  148. Returns:
  149. nib.Nifti1Image: Reoriented volume.
  150. """
  151. current_orientation = "".join(nib.aff2axcodes(img.affine))
  152. if current_orientation == TARGET_ORIENTATION:
  153. return img
  154. logger.debug(f"Reorienting from '{current_orientation}' to '{TARGET_ORIENTATION}'.")
  155. try:
  156. reoriented, _, _, _ = pad_orient_conform(
  157. img,
  158. out_shape=img.shape[:3],
  159. orientation=TARGET_ORIENTATION,
  160. pad=False,
  161. )
  162. return reoriented
  163. except Exception as e:
  164. logger.warning(
  165. f"Reorientation from '{current_orientation}' to '{TARGET_ORIENTATION}' "
  166. f"failed ({e}). Proceeding with original orientation - results may be degraded."
  167. )
  168. return img
  169. def _resample(img: nib.Nifti1Image) -> nib.Nifti1Image:
  170. """Conform image to TARGET_SHAPE and TARGET_ZOOMS.
  171. Three cases are handled in order of cost:
  172. 1. Correct shape and zooms - no-op, return immediately.
  173. 2. Correct zooms, wrong shape - pad and reorient only (fast path, matches
  174. training which assumed pre-conformant inputs from FreeSurfer).
  175. 3. Wrong zooms - full cubic resample via nibabel_conform (slow path). Emits
  176. a one-time UserWarning recommending upstream pre-processing with
  177. `mri_convert --conform --resample_type cubic` for batch use.
  178. Reorientation to TARGET_ORIENTATION is handled as part of both the fast
  179. and slow paths here. _reorient is still called after this function in
  180. preprocess_nifti as a fallback for any residual orientation mismatch.
  181. Parameters
  182. ----------
  183. img : nib.Nifti1Image
  184. Input volume in any shape, resolution, or orientation.
  185. Returns
  186. -------
  187. nib.Nifti1Image
  188. Volume conformed to TARGET_SHAPE and TARGET_ZOOMS where possible.
  189. On failure, returns the original image and logs a warning.
  190. """
  191. current_zooms = tuple(float(z) for z in img.header.get_zooms()[:3])
  192. current_shape = img.shape[:3]
  193. if current_zooms == TARGET_ZOOMS and current_shape == TARGET_SHAPE:
  194. return img
  195. logger.debug(
  196. f"Conforming: shape {current_shape} -> {TARGET_SHAPE}, "
  197. f"zooms {current_zooms} -> {TARGET_ZOOMS}."
  198. )
  199. if current_zooms != TARGET_ZOOMS:
  200. # Full resample required - slow path
  201. warnings.warn(
  202. "Input volume requires resampling due to incorrect voxel size. "
  203. "This is slower and may reduce accuracy. For batch analyses, consider "
  204. "pre-processing with: mri_convert --conform --resample_type cubic <input> <output>",
  205. UserWarning,
  206. stacklevel=2,
  207. )
  208. try:
  209. return nibabel_conform(
  210. img,
  211. out_shape=TARGET_SHAPE,
  212. voxel_size=TARGET_ZOOMS,
  213. order=3,
  214. orientation=TARGET_ORIENTATION,
  215. )
  216. except Exception as e:
  217. logger.warning(
  218. f"Resampling failed ({e}). Proceeding with original resolution - "
  219. f"results may be degraded."
  220. )
  221. return img
  222. else:
  223. # Zooms are correct, only padding/reorientation needed - fast path
  224. try:
  225. reoriented, _, _, _ = pad_orient_conform(
  226. img,
  227. out_shape=TARGET_SHAPE,
  228. orientation=TARGET_ORIENTATION,
  229. pad=True,
  230. )
  231. return reoriented
  232. except Exception as e:
  233. logger.warning(
  234. f"Padding/reorientation failed ({e}). Proceeding with original shape - "
  235. f"results may be degraded."
  236. )
  237. return img
  238. def _normalize(data: np.ndarray) -> np.ndarray:
  239. """z-score for our volumes. Same as during training"""
  240. return stats.zscore(data, axis=None)
  241. # ---------------------------------------------------------------------------
  242. # Output path resolution
  243. # ---------------------------------------------------------------------------
  244. def get_output_dir(
  245. input_path,
  246. output_root,
  247. output_mode: str,
  248. input_root: Optional[str] = None,
  249. individuals_dir: bool = True
  250. ) -> str:
  251. """
  252. Resolve the output directory for a given input file.
  253. Note that for output_mode of 'flat' or 'summary', the path 'individuals' is added by default.
  254. This is to group individual-level outputs within a directory, while the summary files will
  255. be saved in the output root. This can be disabled with individuals_dir = False.
  256. Parameters
  257. ----------
  258. input_path : str
  259. Absolute path to the input NIfTI file.
  260. output_root : str
  261. Root output directory specified by the user.
  262. output_mode : str
  263. One of 'flat', 'mirror', 'summary'.
  264. input_root : str | None
  265. For mirror mode - the common root of all input paths, used to
  266. reconstruct relative directory structure. If None, uses the
  267. input file's parent directory.
  268. Returns
  269. -------
  270. str
  271. Directory where outputs for this input file should be written.
  272. """
  273. if output_mode in ("flat", "summary"):
  274. if individuals_dir:
  275. return Path(output_root) / 'individuals'
  276. else:
  277. return Path(output_root)
  278. # mirror mode: reconstruct relative path under output_root
  279. if input_root is None:
  280. input_root = Path(input_path).parent
  281. try:
  282. rel = Path(input_path).parent.relative_to(input_root)
  283. return Path(output_root) / rel
  284. except ValueError:
  285. # relative_to raises ValueError when path is not under input_root (e.g. different drives on Windows)
  286. logger.warning(
  287. "Could not compute relative path for mirror mode "
  288. f"(input: {input_path}, root: {input_root}). Falling back to flat."
  289. )
  290. return Path(output_root)
  291. def get_expected_output_path(
  292. input_path,
  293. output_root,
  294. output_mode,
  295. input_root:Optional[str] = None,
  296. ) -> str:
  297. """
  298. Return the expected path of the brain_health .npy for a given input.
  299. Used by the CLI to check whether cached outputs exist before running inference.
  300. """
  301. out_dir = get_output_dir(input_path, output_root, output_mode, input_root)
  302. stem = _get_stem(input_path)
  303. return out_dir / f"{stem}_brain_health.npy"
  304. # ---------------------------------------------------------------------------
  305. # Cache loading
  306. # ---------------------------------------------------------------------------
  307. def load_cached_result(
  308. input_path,
  309. output_root,
  310. output_mode,
  311. requested_outputs: List[str],
  312. input_root: Optional[str] = None,
  313. ):
  314. """
  315. Attempt to load previously saved outputs for a given input file.
  316. Returns a results dict (same structure as NeuroFM.predict()) if all
  317. requested outputs are found on disk, otherwise returns None.
  318. """
  319. out_dir = get_output_dir(input_path, output_root, output_mode, input_root)
  320. stem = _get_stem(input_path)
  321. result = {}
  322. if "brain_health" in requested_outputs:
  323. bh_path = out_dir / f"{stem}_brain_health.npy"
  324. if not bh_path.is_file():
  325. return None
  326. result["brain_health"] = np.load(bh_path)
  327. logger.debug(f"Cache hit - loaded brain_health from {bh_path}")
  328. if "latent" in requested_outputs:
  329. lat_path = out_dir / f"{stem}_latent.npy"
  330. if not lat_path.is_file():
  331. return None
  332. result["latent"] = np.load(lat_path)
  333. logger.debug(f"Cache hit - loaded latent from {lat_path}")
  334. return result if result else None
  335. # ---------------------------------------------------------------------------
  336. # Output writing
  337. # ---------------------------------------------------------------------------
  338. def save_outputs(
  339. results: dict,
  340. input_path,
  341. output_root,
  342. requested_outputs: List[str],
  343. output_mode: str = "flat",
  344. input_root = None,
  345. ) -> None:
  346. """
  347. Save per-file model outputs to disk.
  348. Skipped entirely in 'summary' mode - aggregate outputs are written
  349. by save_batch_summary() instead.
  350. Parameters
  351. ----------
  352. results : dict
  353. Output of NeuroFM.predict(), keyed by output name.
  354. input_path : str
  355. Original input file path (used to derive output filename).
  356. output_root : str
  357. Root output directory.
  358. requested_outputs : list[str]
  359. Which outputs to save, e.g. ["brain_health", "latent"].
  360. output_mode : str
  361. One of 'flat', 'mirror', 'summary'.
  362. input_root : str | None
  363. For mirror mode - common root of all input paths.
  364. """
  365. if output_mode == "summary":
  366. return
  367. out_dir = get_output_dir(input_path, output_root, output_mode, input_root)
  368. out_dir.mkdir(exist_ok=True, parents=True)
  369. stem = _get_stem(input_path)
  370. if "brain_health" in requested_outputs and "brain_health" in results:
  371. npy_path = out_dir / f"{stem}_brain_health.npy"
  372. np.save(npy_path, results["brain_health"])
  373. logger.debug(f"Saved brain_health -> {npy_path}")
  374. if "latent" in requested_outputs and "latent" in results:
  375. lat_path = out_dir / f"{stem}_latent.npy"
  376. np.save(lat_path, results["latent"])
  377. logger.debug(f"Saved latent -> {lat_path}")
  378. def save_batch_summary(
  379. all_results: List[dict],
  380. input_paths: list,
  381. output_dir,
  382. requested_outputs: List[str],
  383. input_col:str = "input"
  384. ) -> None:
  385. """
  386. Write aggregate outputs across all processed scans:
  387. - results_summary.csv brain_health values for all scans
  388. - latent_embeddings.npy shape (N, D), if latent was requested
  389. - latent_embeddings_index.csv maps rows of latent_embeddings.npy to input paths
  390. Parameters
  391. ----------
  392. all_results : list[dict]
  393. One result dict per input path. None entries (failed scans) are skipped.
  394. input_paths : list[str]
  395. Input paths in the same order as all_results.
  396. output_dir : str
  397. Directory to write aggregate outputs into.
  398. requested_outputs : list[str]
  399. Which outputs were requested.
  400. """
  401. output_dir = Path(output_dir)
  402. output_dir.mkdir(exist_ok=True, parents=True)
  403. bh_rows = []
  404. latent_rows = []
  405. latent_input_paths = []
  406. for path, result in zip(input_paths, all_results):
  407. if result is None:
  408. continue
  409. if 'brain_health' in result and result['brain_health'] is None \
  410. or 'latent' in result and result['latent'] is None:
  411. continue
  412. if "brain_health" in requested_outputs and "brain_health" in result:
  413. row = {input_col: path}
  414. row.update(dict(zip(BRAIN_HEALTH_KEYS, result["brain_health"].tolist())))
  415. bh_rows.append(row)
  416. if "latent" in requested_outputs and "latent" in result:
  417. latent_rows.append(result["latent"])
  418. latent_input_paths.append(path)
  419. if bh_rows:
  420. summary_path = output_dir / "results_summary.csv"
  421. df = pd.DataFrame(bh_rows)
  422. df.to_csv(summary_path, index=False)
  423. logger.info(f"Summary CSV written -> {summary_path} ({len(bh_rows)} scan(s))")
  424. if latent_rows:
  425. latent_array = np.stack(latent_rows, axis=0) # (N, D)
  426. latent_path = output_dir / "latent_embeddings.npy"
  427. np.save(latent_path, latent_array)
  428. # Index CSV so users can map rows back to input paths
  429. index_path = output_dir / "latent_embeddings_index.csv"
  430. pd.DataFrame({input_col: latent_input_paths}).to_csv(index_path, index=False)
  431. logger.info(
  432. f"Latent embeddings written -> {latent_path} "
  433. f"(shape: {latent_array.shape}), index -> {index_path}"
  434. )
  435. # ---------------------------------------------------------------------------
  436. # Helpers
  437. # ---------------------------------------------------------------------------
  438. def _get_stem(path) -> str:
  439. """Strip directory and NIfTI extensions to get a clean filename stem."""
  440. base = Path(path).name
  441. for ext in (".nii.gz", ".nii"):
  442. if base.endswith(ext):
  443. return base[: -len(ext)]
  444. return base
  445. def infer_input_root(input_paths: list) -> Path:
  446. """
  447. For mirror mode - find the common directory prefix across all input paths.
  448. Returns None if inputs come from completely different trees.
  449. """
  450. if not input_paths:
  451. return None
  452. dirs = [Path(p).parent for p in input_paths]
  453. return Path(os.path.commonpath(dirs)) or None

io.py at commit d4e3c46, under CC-BY-NC-SA-4.0 · at the source

Overview

Authors: Austin Dibble1, Connor Dalby1, Michele Sevegnani2, Alessio Fracasso1,3, Donald M. Lyall4, Monika Harvey1, Michele Svanera1, for the Alzheimer’s Disease Neuroimaging Initiative, for the Frontotemporal Lobar Degeneration Neuroimaging Initiative
  1. School of Psychology & Neuroscience, University of Glasgow, Glasgow, UK
  2. School of Computing Science, University of Glasgow, Glasgow, UK
  3. Department of General Psychology, University of Padova, Padova, Italy
  4. School of Health & Wellbeing, University of Glasgow, Glasgow, UK
Institutions: University of Glasgow (United Kingdom); University of Padua (Italy)
Dates: published online 31 March 2026
Type: Preprint · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.64898/2026.03.27.26349489 · OpenAlex W7143754146
Open access: green, a free copy (OpenAlex)
Status: code verified (how the code is found and verified)
Methods: Statistics, Smoothing, state filtering, decompositions, Machine learning, Preprocessing, fMRI & imaging
Keywords: neuroimaging, foundation model, brain health, precision medicine, precision neuroimaging
Topic: Functional Brain Connectivity Studies (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: Biotechnology and Biological Sciences Research Council (BB/S006605/1)
Citations: not cited yet (Europe PMC); 90 references in the paper
Research resources: IBSR RRID:SCR_001994

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repositories

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

rocknroll87q.github.io/neurofm

License: none: the authors keep all their rights
State: the link answers, verified on 30 September 2026
Evidence: the link answers
Software Heritage: not checked
Found in: the text
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 30 September 2026: the link answers (HTTP 200)
  • 30 September 2026: the link answers (HTTP 200)

rockNroll87q/NeuroFM

License: CC-BY-NC-SA-4.0
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: d4e3c463910d939a681d24ebdeb26d44dea6878f, 10 April 2026
Languages: Python (11), Jupyter (4)
Size: 43 files, 15 scripts
Software Heritage: not archived
Found in: the text, “Foundation model pretraining and model selection”
Holds: README, license file, CITATION.cff, environment (environment.yml, pyproject.toml, requirements.txt, .devcontainer/devcontainer.json, .devcontainer/Dockerfile, .devcontainer/Dockerfile.cpu, docker/Dockerfile, docker/Dockerfile.cpu, docker/Dockerfile.notebook), tests, continuous integration, documentation, 4 notebooks
Tools: NumPy (9 files), pandas (6 files), TensorFlow (5 files), NiBabel (4 files), Matplotlib (3 files), SciPy (2 files), TemplateFlow (2 files), h5py (1 file), Keras (1 file), scikit-learn (1 file), seaborn (1 file), UMAP (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
17 files

Code availability statement

The paper has a code availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

  • no repository, dataset or request procedure was recognized in it

Read it in the paper: doi.org/10.64898/2026.03.27.26349489.

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:

  • 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 15 scripts, each with its path and the digest of its content;
  • 2 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.

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

Recorded: type, language, journal, dates, 9 authors, 5 keywords, 1 funder, 87 references, 1 RRID.

Cite

This paper

Dibble, A., Dalby, C., Sevegnani, M., Fracasso, A., Lyall, D. M., Harvey, M., Svanera, M., for the Alzheimer’s Disease Neuroimaging Initiative, & for the Frontotemporal Lobar Degeneration Neuroimaging Initiative. (2026). NeuroFM: Toward Precision Neuroimaging with Foundation Models for Individualized Brain Health Estimation. medRxiv (preprint). https://doi.org/10.64898/2026.03.27.26349489

BibTeX

@article{dibble2026neurofm,
author = {Dibble, Austin and Dalby, Connor and Sevegnani, Michele and Fracasso, Alessio and Lyall, Donald M. and Harvey, Monika and Svanera, Michele and {for the Alzheimer’s Disease Neuroimaging Initiative} and {for the Frontotemporal Lobar Degeneration Neuroimaging Initiative}},
title = {{NeuroFM: Toward Precision Neuroimaging with Foundation Models for Individualized Brain Health Estimation}},
journal = {medRxiv (preprint)},
year = {2026},
month = mar,
publisher = {medRxiv},
doi = {10.64898/2026.03.27.26349489},
url = {https://doi.org/10.64898/2026.03.27.26349489}
}

RIS

TY - JOUR
AU - Dibble, Austin
AU - Dalby, Connor
AU - Sevegnani, Michele
AU - Fracasso, Alessio
AU - Lyall, Donald M.
AU - Harvey, Monika
AU - Svanera, Michele
AU - for the Alzheimer’s Disease Neuroimaging Initiative
AU - for the Frontotemporal Lobar Degeneration Neuroimaging Initiative
TI - NeuroFM: Toward Precision Neuroimaging with Foundation Models for Individualized Brain Health Estimation
T2 - medRxiv (preprint)
J2 - medRxiv
PY - 2026
DA - 2026/03/31
PB - medRxiv
DO - 10.64898/2026.03.27.26349489
UR - https://doi.org/10.64898/2026.03.27.26349489
LA - en
ER -

CSL-JSON

{
"id": "10.64898/2026.03.27.26349489",
"type": "article",
"title": "NeuroFM: Toward Precision Neuroimaging with Foundation Models for Individualized Brain Health Estimation",
"container-title": "medRxiv (preprint)",
"author": [
{
"family": "Dibble",
"given": "Austin"
},
{
"family": "Dalby",
"given": "Connor"
},
{
"family": "Sevegnani",
"given": "Michele"
},
{
"family": "Fracasso",
"given": "Alessio"
},
{
"family": "Lyall",
"given": "Donald M."
},
{
"family": "Harvey",
"given": "Monika"
},
{
"family": "Svanera",
"given": "Michele"
},
{
"literal": "for the Alzheimer’s Disease Neuroimaging Initiative"
},
{
"literal": "for the Frontotemporal Lobar Degeneration Neuroimaging Initiative"
}
],
"container-title-short": "medRxiv",
"DOI": "10.64898/2026.03.27.26349489",
"publisher": "medRxiv",
"URL": "https://doi.org/10.64898/2026.03.27.26349489",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
31
]
]
}
}

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.64898/2026.08.13.26359403 [code]
NeuroMorph: A Unified Morphological Reference Space for Cross-Disease Brain Profiling
Journal: medRxiv (preprint)
In common: 7 references, 6 authors
[2] doi:10.1038/s41592-025-02599-1 [code]
DeepPrep: an accelerated, scalable and robust pipeline for neuroimaging preprocessing empowered by deep learning.
Journal: Nature methods
In common: TemplateFlow, Keras, TensorFlow, 8 other tools, 3 references
[3] doi:10.1002/hbm.26558 [code]
Brain structure ages-A new biomarker for multi-disease classification.
Journal: Human brain mapping
In common: Keras, TensorFlow, NiBabel, 5 other tools, 6 references
[4] doi:10.1162/netn_a_00382 [code]
Comparison of different group-level templates in gradient-based multimodal connectivity analysis.
Journal: Network neuroscience (Cambridge, Mass.)
In common: TemplateFlow, h5py, NiBabel, 6 other tools, 4 references
[5] doi:10.1002/hbm.26144 [code]
Benchmarking the generalizability of brain age models: Challenges posed by scanner variance and prediction bias.
Journal: Human brain mapping
In common: Keras, TensorFlow, h5py, 7 other tools, 4 references
[6] doi:10.1162/imag_a_00497 [code]
The utility of explainable AI for MRI analysis: Relating model predictions to neuroimaging features of the aging brain.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Keras, TensorFlow, NiBabel, 6 other tools, 4 references
[7] doi:10.3389/fneur.2022.960760 [code]
Confounds in neuroimaging: A clear case of sex as a confound in brain-based prediction.
Journal: Frontiers in neurology
In common: Keras, TensorFlow, NiBabel, 6 other tools, 4 references
[8] doi:10.1186/s13244-025-02158-4 [code]
Skull-stripping induces shortcut learning in MRI-based Alzheimer's disease classification.
Journal: Insights into imaging
In common: NiBabel, seaborn, scikit-learn, 4 other tools, 5 references
[9] doi:10.7554/elife.97036 [code]
Rate of brain aging associates with future executive function in Asian children and older adults.
Journal: eLife
In common: Keras, TensorFlow, NiBabel, 6 other tools, 4 references
[10] doi:10.1016/j.neuroimage.2023.120109 [code]
Benchmarking explanation methods for mental state decoding with deep learning models.
Journal: NeuroImage
In common: TemplateFlow, h5py, NiBabel, 6 other tools, 3 references

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.

Add the badge to the code's README

OSCR: the badge of this paper

If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.

Markdown
<!-- oscr-badge:start -->
[![OSCR](https://openscicode.org/badge/doi/10.64898/2026.03.27.26349489.svg)](https://openscicode.org/badge/doi/10.64898/2026.03.27.26349489)
<!-- oscr-badge:end -->
reStructuredText
.. oscr-badge:start

.. image:: https://openscicode.org/badge/doi/10.64898/2026.03.27.26349489.svg
   :target: https://openscicode.org/badge/doi/10.64898/2026.03.27.26349489
   :alt: OSCR

.. oscr-badge:end
HTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.64898/2026.03.27.26349489"><img src="https://openscicode.org/badge/doi/10.64898/2026.03.27.26349489.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->

On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".

If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge 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: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.

Report a mismatch between the code and the paper · Report a code error (signed in)

Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.

Report a reproduction failure (signed in)

Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.

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