Coverage for nilearn/datasets/struct.py: 18%
207 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-20 10:58 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-20 10:58 +0200
1"""Downloading NeuroImaging datasets: structural datasets."""
3import functools
4import warnings
5from pathlib import Path
7import numpy as np
8import pandas as pd
9from scipy.ndimage import binary_closing
10from sklearn.utils import Bunch
12from nilearn._utils import check_niimg, fill_doc
13from nilearn._utils.logger import find_stack_level
14from nilearn._utils.param_validation import check_params
15from nilearn.datasets._utils import (
16 ALLOWED_DATA_TYPES,
17 ALLOWED_MESH_TYPES,
18 PACKAGE_DIRECTORY,
19 fetch_files,
20 get_dataset_descr,
21 get_dataset_dir,
22)
23from nilearn.image import get_data, new_img_like, resampling
24from nilearn.surface import (
25 FileMesh,
26 PolyMesh,
27 SurfaceImage,
28)
30MNI152_FILE_PATH = (
31 PACKAGE_DIRECTORY
32 / "data"
33 / "mni_icbm152_t1_tal_nlin_sym_09a_converted.nii.gz"
34)
35GM_MNI152_FILE_PATH = (
36 PACKAGE_DIRECTORY
37 / "data"
38 / "mni_icbm152_gm_tal_nlin_sym_09a_converted.nii.gz"
39)
40WM_MNI152_FILE_PATH = (
41 PACKAGE_DIRECTORY
42 / "data"
43 / "mni_icbm152_wm_tal_nlin_sym_09a_converted.nii.gz"
44)
45FSAVERAGE5_PATH = PACKAGE_DIRECTORY / "data" / "fsaverage5"
48@fill_doc
49def fetch_icbm152_2009(data_dir=None, url=None, resume=True, verbose=1):
50 """Download and load the ICBM152 template (dated 2009).
52 %(templateflow)s
54 Parameters
55 ----------
56 %(data_dir)s
57 %(url)s
58 %(resume)s
59 %(verbose)s
61 Returns
62 -------
63 data : :obj:`sklearn.utils.Bunch`
64 Dictionary-like object, interest keys are:
66 - "t1": str,
67 Path to T1-weighted anatomical image
68 - "t2": str,
69 Path to T2-weighted anatomical image
70 - "t2_relax": str,
71 Path to anatomical image obtained with the T2 relaxometry
72 - "pd": str,
73 Path to the proton density weighted anatomical image
74 - "gm": str,
75 Path to gray matter segmented image
76 - "wm": str,
77 Path to white matter segmented image
78 - "csf": str,
79 Path to cerebrospinal fluid segmented image
80 - "eye_mask": str,
81 Path to eye mask useful to mask out part of MRI images
82 - "face_mask": str,
83 Path to face mask useful to mask out part of MRI images
84 - "mask": str,
85 Path to whole brain mask useful to mask out skull areas
87 See Also
88 --------
89 nilearn.datasets.load_mni152_template: to load MNI152 T1 template.
91 nilearn.datasets.load_mni152_gm_template: to load MNI152 gray matter
92 template.
94 nilearn.datasets.load_mni152_wm_template: to load MNI152 white matter
95 template.
97 nilearn.datasets.load_mni152_brain_mask: to load MNI152 whole brain mask.
99 nilearn.datasets.load_mni152_gm_mask: to load MNI152 gray matter mask.
101 nilearn.datasets.load_mni152_wm_mask: to load MNI152 white matter mask.
103 nilearn.datasets.fetch_icbm152_brain_gm_mask: to fetch only ICBM gray
104 matter mask.
106 Notes
107 -----
108 For more information
109 see the :ref:`dataset description <icbm_152_template>`.
111 """
112 check_params(locals())
114 if url is None:
115 # The URL can be retrieved from the nilearn account on OSF (Open
116 # Science Framework), https://osf.io/4r3jt/quickfiles/
117 # Clicking on the "share" button gives the root of the URL.
118 url = "https://osf.io/7pj92/download"
119 opts = {"uncompress": True}
121 keys = (
122 "csf",
123 "gm",
124 "wm",
125 "pd",
126 "t1",
127 "t2",
128 "t2_relax",
129 "eye_mask",
130 "face_mask",
131 "mask",
132 )
133 filenames = [
134 (Path("mni_icbm152_nlin_sym_09a", name), url, opts)
135 for name in (
136 "mni_icbm152_csf_tal_nlin_sym_09a.nii.gz",
137 "mni_icbm152_gm_tal_nlin_sym_09a.nii.gz",
138 "mni_icbm152_wm_tal_nlin_sym_09a.nii.gz",
139 "mni_icbm152_pd_tal_nlin_sym_09a.nii.gz",
140 "mni_icbm152_t1_tal_nlin_sym_09a.nii.gz",
141 "mni_icbm152_t2_tal_nlin_sym_09a.nii.gz",
142 "mni_icbm152_t2_relx_tal_nlin_sym_09a.nii.gz",
143 "mni_icbm152_t1_tal_nlin_sym_09a_eye_mask.nii.gz",
144 "mni_icbm152_t1_tal_nlin_sym_09a_face_mask.nii.gz",
145 "mni_icbm152_t1_tal_nlin_sym_09a_mask.nii.gz",
146 )
147 ]
149 dataset_name = "icbm152_2009"
150 data_dir = get_dataset_dir(
151 dataset_name, data_dir=data_dir, verbose=verbose
152 )
153 sub_files = fetch_files(
154 data_dir, filenames, resume=resume, verbose=verbose
155 )
157 fdescr = get_dataset_descr(dataset_name)
159 params = dict([("description", fdescr), *list(zip(keys, sub_files))])
160 return Bunch(**params)
163@functools.lru_cache(maxsize=3)
164@fill_doc
165def load_mni152_template(resolution=None):
166 """Load the MNI152 skullstripped T1 template.
168 This function takes the skullstripped,
169 re-scaled 1mm-resolution version of the :term:`MNI` ICBM152 T1 template
170 and re-samples it using a different resolution, if specified.
172 %(templateflow)s
174 Parameters
175 ----------
176 %(resolution)s
178 .. versionadded:: 0.8.1
180 Returns
181 -------
182 mni152_template : Nifti1Image, image representing the re-sampled
183 whole-brain template
185 See Also
186 --------
187 nilearn.datasets.fetch_icbm152_2009: for details regarding the difference
188 between NiLearn and :term:`fMRIPrep` ICBM152 template.
190 nilearn.datasets.load_mni152_gm_template : for details about version of the
191 MNI152 grey-matter template.
193 nilearn.datasets.load_mni152_wm_template : for details about version of the
194 MNI152 white-matter template.
196 Notes
197 -----
198 For more information
199 see the :ref:`dataset description <icbm_152_template>`.
201 """
202 resolution = resolution or 1
204 brain_template = check_niimg(MNI152_FILE_PATH)
206 # Typecasting
207 brain_data = get_data(brain_template).astype("float32")
209 # Re-scale template from 0 to 1
210 brain_data /= brain_data.max()
211 new_brain_template = new_img_like(brain_template, brain_data)
213 # Resample template according to the pre-specified resolution, if different
214 # than 1
215 if resolution != 1:
216 # TODO switch to force_resample=True
217 # when bumping to version > 0.13
218 new_brain_template = resampling.resample_img(
219 new_brain_template,
220 np.eye(3) * resolution,
221 copy_header=True,
222 force_resample=False,
223 )
225 return new_brain_template
228@fill_doc
229def load_mni152_gm_template(resolution=None):
230 """Load the MNI152 grey-matter template.
232 This function takes the re-scaled 1mm-resolution version of the grey-matter
233 MNI ICBM152 template and re-samples it using a different resolution,
234 if specified.
236 %(templateflow)s
238 .. versionadded:: 0.8.1
240 Parameters
241 ----------
242 %(resolution)s
244 Returns
245 -------
246 gm_mni152_template : Nifti1Image, image representing the resampled
247 grey-matter template
249 See Also
250 --------
251 nilearn.datasets.load_mni152_template : for details about version of the
252 MNI152 T1 template.
254 nilearn.datasets.load_mni152_wm_template : for details about version of the
255 MNI152 white-matter template.
257 Notes
258 -----
259 For more information
260 see the :ref:`dataset description <icbm_152_template>`.
262 """
263 resolution = resolution or 1
265 gm_template = check_niimg(GM_MNI152_FILE_PATH)
267 # Typecasting
268 gm_data = get_data(gm_template).astype("float32")
270 # Re-scale template from 0 to 1
271 gm_data /= gm_data.max()
272 new_gm_template = new_img_like(gm_template, gm_data)
274 # Resample template according to the pre-specified resolution, if different
275 # than 1
276 if resolution != 1:
277 # TODO switch to force_resample=True
278 # when bumping to version > 0.13
279 new_gm_template = resampling.resample_img(
280 new_gm_template,
281 np.eye(3) * resolution,
282 copy_header=True,
283 force_resample=False,
284 )
286 return new_gm_template
289@fill_doc
290def load_mni152_wm_template(resolution=None):
291 """Load the MNI152 white-matter template.
293 This function takes the re-scaled 1mm-resolution version of the
294 white-matter :term:`MNI` ICBM152 template
295 and re-samples it using a different
296 resolution, if specified.
298 %(templateflow)s
300 .. versionadded:: 0.8.1
302 Parameters
303 ----------
304 %(resolution)s
306 Returns
307 -------
308 wm_mni152_template : Nifti1Image, image representing the resampled
309 white-matter template
311 See Also
312 --------
313 nilearn.datasets.load_mni152_template : for details about version of the
314 MNI152 T1 template.
316 nilearn.datasets.load_mni152_gm_template : for details about version of the
317 MNI152 grey-matter template.
319 Notes
320 -----
321 For more information
322 see the :ref:`dataset description <icbm_152_template>`.
324 """
325 resolution = resolution or 1
327 wm_template = check_niimg(WM_MNI152_FILE_PATH)
329 # Typecasting
330 wm_data = get_data(wm_template).astype("float32")
332 # Re-scale template from 0 to 1
333 wm_data /= wm_data.max()
334 new_wm_template = new_img_like(wm_template, wm_data)
336 # Resample template according to the pre-specified resolution, if different
337 # than 1
338 if resolution != 1:
339 # TODO switch to force_resample=True
340 # when bumping to version > 0.13
341 new_wm_template = resampling.resample_img(
342 new_wm_template,
343 np.eye(3) * resolution,
344 copy_header=True,
345 force_resample=False,
346 )
348 return new_wm_template
351@fill_doc
352def load_mni152_brain_mask(resolution=None, threshold=0.2):
353 """Load the MNI152 whole-brain mask.
355 This function takes the whole-brain MNI152 T1 template and threshold it,
356 in order to obtain the corresponding whole-brain mask.
358 %(templateflow)s
360 .. versionadded:: 0.2.5
362 Parameters
363 ----------
364 %(resolution)s
366 .. versionadded:: 0.8.1
368 threshold : :obj:`float`, default=0.2
369 Values of the MNI152 T1 template above this threshold will be included.
371 Returns
372 -------
373 mask_img : Nifti1Image, image corresponding to the whole-brain mask.
375 See Also
376 --------
377 nilearn.datasets.load_mni152_template : for details about version of the
378 MNI152 T1 template and related.
380 Notes
381 -----
382 For more information
383 see the :ref:`dataset description <icbm_152_template>`.
385 """
386 resolution = resolution or 1
388 # Load MNI template
389 target_img = load_mni152_template(resolution=resolution)
390 mask_voxels = (get_data(target_img) > threshold).astype("int8")
391 mask_img = new_img_like(target_img, mask_voxels)
393 return mask_img
396@fill_doc
397def load_mni152_gm_mask(resolution=None, threshold=0.2, n_iter=2):
398 """Load the MNI152 grey-matter mask.
400 This function takes the grey-matter MNI152 template and threshold it, in
401 order to obtain the corresponding grey-matter mask.
403 %(templateflow)s
405 .. versionadded:: 0.8.1
407 Parameters
408 ----------
409 %(resolution)s
411 threshold : :obj:`float`, default=0.2
412 Values of the grey-matter MNI152 template above this threshold will be
413 included.
415 n_iter : :obj:`int`, default=2
416 Number of repetitions of :term:`dilation<Dilation>`
417 and :term:`erosion<Erosion>` steps performed in
418 scipy.ndimage.binary_closing function.
420 Returns
421 -------
422 gm_mask_img : Nifti1Image, image corresponding to the grey-matter mask.
424 See Also
425 --------
426 nilearn.datasets.load_mni152_gm_template : for details about version of the
427 MNI152 grey-matter template and related.
429 Notes
430 -----
431 For more information
432 see the :ref:`dataset description <icbm_152_template>`.
434 """
435 resolution = resolution or 1
437 # Load MNI template
438 gm_target = load_mni152_gm_template(resolution=resolution)
439 gm_target_img = check_niimg(gm_target)
440 gm_target_data = get_data(gm_target_img)
442 gm_target_mask = (gm_target_data > threshold).astype("int8")
444 gm_target_mask = binary_closing(gm_target_mask, iterations=n_iter)
445 gm_mask_img = new_img_like(gm_target_img, gm_target_mask)
447 return gm_mask_img
450@fill_doc
451def load_mni152_wm_mask(resolution=None, threshold=0.2, n_iter=2):
452 """Load the MNI152 white-matter mask.
454 This function takes the white-matter MNI152 template and threshold it, in
455 order to obtain the corresponding white-matter mask.
457 %(templateflow)s
459 .. versionadded:: 0.8.1
461 Parameters
462 ----------
463 %(resolution)s
465 threshold : :obj:`float`, default=0.2
466 Values of the white-matter MNI152 template above this threshold will be
467 included.
469 n_iter : :obj:`int`, default=2
470 Number of repetitions of :term:`dilation<Dilation>`
471 and :term:`erosion<Erosion>` steps performed in
472 scipy.ndimage.binary_closing function.
474 Returns
475 -------
476 wm_mask_img : Nifti1Image, image corresponding to the white-matter mask.
478 See Also
479 --------
480 nilearn.datasets.load_mni152_wm_template : for details about version of the
481 MNI152 white-matter template and related.
483 Notes
484 -----
485 For more information
486 see the :ref:`dataset description <icbm_152_template>`.
488 """
489 resolution = resolution or 1
491 # Load MNI template
492 wm_target = load_mni152_wm_template(resolution=resolution)
493 wm_target_img = check_niimg(wm_target)
494 wm_target_data = get_data(wm_target_img)
496 wm_target_mask = (wm_target_data > threshold).astype("int8")
498 wm_target_mask = binary_closing(wm_target_mask, iterations=n_iter)
499 wm_mask_img = new_img_like(wm_target_img, wm_target_mask)
501 return wm_mask_img
504@fill_doc
505def fetch_icbm152_brain_gm_mask(
506 data_dir=None, threshold=0.2, resume=True, n_iter=2, verbose=1
507):
508 """Download ICBM152 template first, then loads the 'gm' mask.
510 %(templateflow)s
512 .. versionadded:: 0.2.5
514 Parameters
515 ----------
516 %(data_dir)s
518 threshold : :obj:`float`, default=0.2
519 Values of the ICBM152 grey-matter template above this threshold will be
520 included.
522 %(resume)s
524 n_iter : :obj:`int`, default=2
525 Number of repetitions of :term:`dilation<Dilation>`
526 and :term:`erosion<Erosion>` steps performed in
527 scipy.ndimage.binary_closing function.
529 .. versionadded:: 0.8.1
531 %(verbose)s
533 Returns
534 -------
535 gm_mask_img : Nifti1Image, image corresponding to the brain gray matter
536 from ICBM152 template.
538 See Also
539 --------
540 nilearn.datasets.fetch_icbm152_2009: for details regarding the ICBM152
541 template.
543 nilearn.datasets.load_mni152_template: for details about version of MNI152
544 template and related.
546 Notes
547 -----
548 This function relies on ICBM152 templates where we particularly pick
549 gray matter template and threshold the template at .2 to take one fifth
550 of the values. Then, do a bit post processing such as binary closing
551 operation to more compact mask image.
553 .. note::
554 It is advised to check the mask image with your own data processing.
556 For more information
557 see the :ref:`dataset description <icbm_152_template>`.
559 """
560 check_params(locals())
562 # Fetching ICBM152 gray matter mask image
563 icbm = fetch_icbm152_2009(
564 data_dir=data_dir, resume=resume, verbose=verbose
565 )
566 gm = icbm["gm"]
567 gm_img = check_niimg(gm)
568 gm_data = get_data(gm_img)
570 # getting one fifth of the values
571 gm_mask = (gm_data > threshold).astype("int8")
573 gm_mask = binary_closing(gm_mask, iterations=n_iter)
574 gm_mask_img = new_img_like(gm_img, gm_mask)
576 return gm_mask_img
579def oasis_missing_subjects():
580 """Return list of missing subjects in OASIS dataset."""
581 return [
582 8,
583 24,
584 36,
585 48,
586 89,
587 93,
588 100,
589 118,
590 128,
591 149,
592 154,
593 171,
594 172,
595 175,
596 187,
597 194,
598 196,
599 215,
600 219,
601 225,
602 242,
603 245,
604 248,
605 251,
606 252,
607 257,
608 276,
609 297,
610 306,
611 320,
612 324,
613 334,
614 347,
615 360,
616 364,
617 391,
618 393,
619 412,
620 414,
621 427,
622 436,
623 ]
626@fill_doc
627def fetch_oasis_vbm(
628 n_subjects=None,
629 dartel_version=True,
630 data_dir=None,
631 url=None,
632 resume=True,
633 verbose=1,
634):
635 """Download and load Oasis "cross-sectional MRI" dataset (416 subjects).
637 .. admonition:: Data Usage Agreement
638 :class: attention
640 Using data available through the OASIS project requires agreeing with
641 the Data Usage Agreement that can be found at
642 https://sites.wustl.edu/oasisbrains/
644 Parameters
645 ----------
646 n_subjects : :obj:`int`, default=None
647 The number of subjects to load. If None is given, all the
648 subjects are used.
650 dartel_version : :obj:`bool`, default=True
651 Whether or not to use data normalized with DARTEL instead of standard
652 SPM8 normalization.
653 %(data_dir)s
654 %(url)s
655 %(resume)s
656 %(verbose)s
658 Returns
659 -------
660 data : Bunch
661 Dictionary-like object, the interest attributes are :
663 - 'gray_matter_maps': string list
664 Paths to nifti gray matter density probability maps
665 - 'white_matter_maps' string list
666 Paths to nifti white matter density probability maps
667 - 'ext_vars': pandas.DataFrame
668 Data from the .csv file with information about selected subjects
669 - 'data_usage_agreement': string
670 Path to the .txt file containing the data usage agreement.
672 Notes
673 -----
674 For more information
675 see the :ref:`dataset description <oasis_maps>`.
677 """
678 check_params(locals())
680 # check number of subjects
681 if n_subjects is None:
682 n_subjects = 403 if dartel_version else 415
683 if dartel_version: # DARTEL version has 13 identified outliers
684 if n_subjects > 403:
685 warnings.warn(
686 "Only 403 subjects are available in the "
687 "DARTEL-normalized version of the dataset. "
688 f"All of them will be used instead of the wanted {n_subjects}",
689 stacklevel=find_stack_level(),
690 )
691 n_subjects = 403
692 elif n_subjects > 415:
693 warnings.warn(
694 "Only 415 subjects are available in the "
695 "non-DARTEL-normalized version of the dataset. "
696 f"All of them will be used instead of the wanted {n_subjects}",
697 stacklevel=find_stack_level(),
698 )
699 n_subjects = 415
700 if n_subjects < 1:
701 raise ValueError(f"Incorrect number of subjects ({n_subjects})")
703 # pick the archive corresponding to preprocessings type
704 if url is None:
705 if dartel_version:
706 url_images = (
707 "https://www.nitrc.org/frs/download.php/"
708 "6364/archive_dartel.tgz?i_agree=1&download_now=1"
709 )
710 else:
711 url_images = (
712 "https://www.nitrc.org/frs/download.php/"
713 "6359/archive.tgz?i_agree=1&download_now=1"
714 )
715 # covariates and license are in separate files on NITRC
716 url_csv = (
717 "https://www.nitrc.org/frs/download.php/"
718 "6348/oasis_cross-sectional.csv?i_agree=1&download_now=1"
719 )
720 url_dua = (
721 "https://www.nitrc.org/frs/download.php/"
722 "6349/data_usage_agreement.txt?i_agree=1&download_now=1"
723 )
724 else: # local URL used in tests
725 url_csv = url + "/oasis_cross-sectional.csv"
726 url_dua = url + "/data_usage_agreement.txt"
727 if dartel_version:
728 url_images = url + "/archive_dartel.tgz"
729 else:
730 url_images = url + "/archive.tgz"
732 opts = {"uncompress": True}
734 # missing subjects create shifts in subjects ids
735 missing_subjects = oasis_missing_subjects()
737 if dartel_version:
738 # DARTEL produces outliers that are hidden by nilearn API
739 removed_outliers = [
740 27,
741 57,
742 66,
743 83,
744 122,
745 157,
746 222,
747 269,
748 282,
749 287,
750 309,
751 428,
752 ]
753 missing_subjects = sorted(missing_subjects + removed_outliers)
754 file_names_gm = [
755 (
756 Path(
757 f"OAS1_{s:04d}_MR1",
758 f"mwrc1OAS1_{s:04d}_MR1_mpr_anon_fslswapdim_bet.nii.gz",
759 ),
760 url_images,
761 opts,
762 )
763 for s in range(1, 457)
764 if s not in missing_subjects
765 ][:n_subjects]
766 file_names_wm = [
767 (
768 Path(
769 f"OAS1_{s:04d}_MR1",
770 f"mwrc2OAS1_{s:04d}_MR1_mpr_anon_fslswapdim_bet.nii.gz",
771 ),
772 url_images,
773 opts,
774 )
775 for s in range(1, 457)
776 if s not in missing_subjects
777 ]
778 else:
779 # only one gross outlier produced, hidden by nilearn API
780 removed_outliers = [390]
781 missing_subjects = sorted(missing_subjects + removed_outliers)
782 file_names_gm = [
783 (
784 Path(
785 f"OAS1_{s:04d}_MR1",
786 f"mwc1OAS1_{s:04d}_MR1_mpr_anon_fslswapdim_bet.nii.gz",
787 ),
788 url_images,
789 opts,
790 )
791 for s in range(1, 457)
792 if s not in missing_subjects
793 ][:n_subjects]
794 file_names_wm = [
795 (
796 Path(
797 f"OAS1_{s:04d}_MR1",
798 f"mwc2OAS1_{s:04d}_MR1_mpr_anon_fslswapdim_bet.nii.gz",
799 ),
800 url_images,
801 opts,
802 )
803 for s in range(1, 457)
804 if s not in missing_subjects
805 ]
806 file_names_extvars = [("oasis_cross-sectional.csv", url_csv, {})]
807 file_names_dua = [("data_usage_agreement.txt", url_dua, {})]
808 # restrict to user-specified number of subjects
809 file_names_gm = file_names_gm[:n_subjects]
810 file_names_wm = file_names_wm[:n_subjects]
812 file_names = (
813 file_names_gm + file_names_wm + file_names_extvars + file_names_dua
814 )
815 dataset_name = "oasis1"
816 data_dir = get_dataset_dir(
817 dataset_name, data_dir=data_dir, verbose=verbose
818 )
819 files = fetch_files(data_dir, file_names, resume=resume, verbose=verbose)
821 # Build Bunch
822 gm_maps = files[:n_subjects]
823 wm_maps = files[n_subjects : (2 * n_subjects)]
824 ext_vars_file = files[-2]
825 data_usage_agreement = files[-1]
827 # Keep CSV information only for selected subjects
828 csv_data = pd.read_csv(ext_vars_file)
829 # Comparisons to recfromcsv data must be bytes.
830 actual_subjects_ids = [
831 ("OAS1" + str.split(Path(x).name, "OAS1")[1][:9]) for x in gm_maps
832 ]
833 subject_mask = np.asarray(
834 [subject_id in actual_subjects_ids for subject_id in csv_data["ID"]]
835 )
836 csv_data = csv_data[subject_mask]
837 csv_data = csv_data.rename(
838 columns={c: c.lower().replace("/", "") for c in csv_data.columns}
839 )
840 fdescr = get_dataset_descr(dataset_name)
842 return Bunch(
843 gray_matter_maps=gm_maps,
844 white_matter_maps=wm_maps,
845 ext_vars=csv_data,
846 data_usage_agreement=data_usage_agreement,
847 description=fdescr,
848 )
851@fill_doc
852def fetch_surf_fsaverage(mesh="fsaverage5", data_dir=None):
853 """Download a Freesurfer fsaverage surface.
855 File names are subject to change and only attribute names
856 are guaranteed to be stable across nilearn versions.
857 See :footcite:t:`Fischl1999`.
859 See :func:`~nilearn.datasets.load_fsaverage` and
860 :func:`~nilearn.datasets.load_fsaverage_data`
861 to access fsaverage data as :obj:`~nilearn.surface.SurfaceImage`.
863 Parameters
864 ----------
865 mesh : :obj:`str`, default='fsaverage5'
866 Which :term:`mesh` to fetch.
867 Should be one of the following values:
868 %(fsaverage_options)s
870 %(data_dir)s
872 Returns
873 -------
874 data : :obj:`sklearn.utils.Bunch`
875 Dictionary-like object, the interest attributes are :
876 - 'area_left': Gifti file, left hemisphere area data
877 - 'area_right': Gifti file, right hemisphere area data
878 - 'curv_left': Gifti file, left hemisphere curvature data
879 - 'curv_right': Gifti file, right hemisphere curvature data
880 - 'flat_left': Gifti file, left hemisphere flat surface :term:`mesh`
881 - 'flat_right': Gifti file, right hemisphere flat surface :term:`mesh`
882 - 'pial_left': Gifti file, left hemisphere pial surface :term:`mesh`
883 - 'pial_right': Gifti file, right hemisphere pial surface :term:`mesh`
884 - 'infl_left': Gifti file, left hemisphere inflated pial surface
885 :term:`mesh`
886 - 'infl_right': Gifti file, right hemisphere inflated pial
887 surface :term:`mesh`
888 - 'sphere_left': Gifti file, left hemisphere sphere surface
889 :term:`mesh`
890 - 'sphere_right': Gifti file, right hemisphere sphere surface
891 :term:`mesh`
892 - 'sulc_left': Gifti file, left hemisphere sulcal depth data
893 - 'sulc_right': Gifti file, right hemisphere sulcal depth data
894 - 'thick_left': Gifti file, left hemisphere cortical thickness data
895 - 'thick_right': Gifti file, right hemisphere cortical thickness data
896 - 'white_left': Gifti file, left hemisphere
897 white surface :term:`mesh`
898 - 'white_right': Gifti file, right hemisphere*
899 white surface :term:`mesh`
901 References
902 ----------
903 .. footbibliography::
905 """
906 check_params(locals())
908 available_meshes = (
909 "fsaverage3",
910 "fsaverage4",
911 "fsaverage5",
912 "fsaverage6",
913 "fsaverage7",
914 "fsaverage",
915 )
917 if mesh not in available_meshes:
918 raise ValueError(
919 f"'mesh' should be one of {available_meshes}; "
920 f"{mesh!r} was provided"
921 )
923 # Call a dataset loader depending on the value of mesh
924 if mesh in (
925 "fsaverage3",
926 "fsaverage4",
927 "fsaverage6",
928 "fsaverage7",
929 "fsaverage",
930 ):
931 # rename mesh to "fsaverage" to download it once
932 # regardless of whether mesh equals "fsaverage" or "fsaverage7"
933 if mesh == "fsaverage7":
934 mesh = "fsaverage"
935 bunch = _fetch_surf_fsaverage(mesh, data_dir=data_dir)
936 elif mesh == "fsaverage5":
937 bunch = _fetch_surf_fsaverage5()
939 return bunch
942def _fetch_surf_fsaverage5():
943 """Ship fsaverage5 surfaces and sulcal information with Nilearn.
945 The source of the data is coming from nitrc based on this PR #1016.
946 Manually downloaded gzipped and shipped with this function.
948 Shipping is done with Nilearn based on issue #1705.
950 """
951 data_dir = Path(FSAVERAGE5_PATH)
953 data = {
954 f"{part}_{hemi}": str(data_dir / f"{part}_{hemi}.gii.gz")
955 for part in [
956 "area",
957 "curv",
958 "flat",
959 "infl",
960 "pial",
961 "sphere",
962 "sulc",
963 "thick",
964 "white",
965 ]
966 for hemi in ["left", "right"]
967 }
968 data["description"] = get_dataset_descr("fsaverage5")
970 return Bunch(**data)
973def _fetch_surf_fsaverage(dataset_name, data_dir=None):
974 """Ship fsaverage{3,4,6,7} meshes.
976 These meshes can be used for visualization purposes, but also to run
977 cortical surface-based searchlight decoding.
979 The source of the data is downloaded from OSF.
980 """
981 dataset_dir = get_dataset_dir(dataset_name, data_dir=data_dir)
982 opts = {"uncompress": True}
984 url = {
985 "fsaverage3": "https://osf.io/azhdf/download",
986 "fsaverage4": "https://osf.io/28uma/download",
987 "fsaverage6": "https://osf.io/jzxyr/download",
988 "fsaverage": "https://osf.io/svf8k/download", # fsaverage7
989 }[dataset_name]
991 # List of attributes exposed by the dataset
992 dataset_attributes = [
993 f"{part}_{hemi}"
994 for part in [
995 "area",
996 "curv",
997 "flat",
998 "infl",
999 "pial",
1000 "sphere",
1001 "sulc",
1002 "thick",
1003 "white",
1004 ]
1005 for hemi in ["left", "right"]
1006 ]
1008 # Note that the file names match the attribute's
1009 fetch_files(
1010 dataset_dir,
1011 [
1012 (f"{attribute}.gii.gz", url, opts)
1013 for attribute in dataset_attributes
1014 ],
1015 )
1017 result = {
1018 attribute: dataset_dir / f"{attribute}.gii.gz"
1019 for attribute in dataset_attributes
1020 }
1021 result["description"] = str(get_dataset_descr(dataset_name))
1023 return Bunch(**result)
1026@fill_doc
1027def load_fsaverage(mesh="fsaverage5", data_dir=None):
1028 """Load fsaverage for both hemispheres as PolyMesh objects.
1030 .. versionadded:: 0.11.0
1032 Parameters
1033 ----------
1034 mesh : :obj:`str`, default='fsaverage5'
1035 Which :term:`mesh` to fetch.
1036 Should be one of the following values:
1037 %(fsaverage_options)s
1039 %(data_dir)s
1041 Returns
1042 -------
1043 data : :obj:`sklearn.utils.Bunch`
1044 Dictionary-like object, the interest attributes are :
1045 - ``'description'``: description of the dataset
1046 - ``'pial'``: Polymesh for pial surface for left and right hemispheres
1047 - ``'white_matter'``: Polymesh for white matter surface
1048 for left and right hemispheres
1049 - ``'inflated'``: Polymesh for inglated surface
1050 for left and right hemispheres
1051 - ``'sphere'``: Polymesh for spherical surface
1052 for left and right hemispheres
1053 - ``'flat'``: Polymesh for flattened surface
1054 for left and right hemispheres
1055 """
1056 check_params(locals())
1058 fsaverage = fetch_surf_fsaverage(mesh, data_dir=data_dir)
1059 renaming = {
1060 "pial": "pial",
1061 "white": "white_matter",
1062 "infl": "inflated",
1063 "sphere": "sphere",
1064 "flat": "flat",
1065 }
1066 meshes = {"description": fsaverage.description}
1067 for key, value in renaming.items():
1068 left = FileMesh(fsaverage[f"{key}_left"])
1069 right = FileMesh(fsaverage[f"{key}_right"])
1070 meshes[value] = PolyMesh(left=left, right=right)
1071 return Bunch(**meshes)
1074@fill_doc
1075def load_fsaverage_data(
1076 mesh="fsaverage5", mesh_type="pial", data_type="sulcal", data_dir=None
1077):
1078 """Return freesurfer data on an fsaverage mesh as a SurfaceImage.
1080 .. versionadded:: 0.11.0
1082 Parameters
1083 ----------
1084 mesh : :obj:`str`, default='fsaverage5'
1085 Which :term:`mesh` to fetch.
1086 Should be one of the following values:
1087 %(fsaverage_options)s
1089 mesh_type : :obj:`str`, default='pial'
1090 Must be one of:
1091 - ``"pial"``
1092 - ``"white_matter"``
1093 - ``"inflated"``
1094 - ``"sphere"``
1095 - ``"flat"``
1097 data_type : :obj:`str`, default='sulcal'
1098 Must be one of:
1099 - ``"curvature"``,
1100 - ``"sulcal"``,
1101 - ``"thickness"``,
1103 %(data_dir)s
1105 Returns
1106 -------
1107 img : :obj:`~nilearn.surface.SurfaceImage`
1108 SurfaceImage with the freesurfer mesh and data.
1109 """
1110 check_params(locals())
1112 if mesh_type not in ALLOWED_MESH_TYPES:
1113 raise ValueError(
1114 f"'mesh_type' must be one of {ALLOWED_MESH_TYPES}.\n"
1115 f"Got: {mesh_type=}."
1116 )
1117 if data_type not in ALLOWED_DATA_TYPES:
1118 raise ValueError(
1119 f"'data_type' must be one of {ALLOWED_DATA_TYPES}.\n"
1120 f"Got: {data_type=}."
1121 )
1123 fsaverage = load_fsaverage(mesh=mesh, data_dir=data_dir)
1124 fsaverage_data = fetch_surf_fsaverage(mesh=mesh, data_dir=data_dir)
1125 renaming = {"curvature": "curv", "sulcal": "sulc", "thickness": "thick"}
1126 img = SurfaceImage(
1127 mesh=fsaverage[mesh_type],
1128 data={
1129 "left": fsaverage_data[f"{renaming[data_type]}_left"],
1130 "right": fsaverage_data[f"{renaming[data_type]}_right"],
1131 },
1132 )
1134 return img