1"""
2Downloading NeuroImaging datasets: structural datasets
3
4# License: simplified BSD
5"""
6
7import warnings
8import os
9import functools
10from pathlib import Path
11
12import numpy as np
13from scipy import ndimage
14from sklearn.utils import Bunch
15
16from .utils import (_get_dataset_dir, _fetch_files, _get_dataset_descr)
17
18from ..image import new_img_like, get_data, resampling
19from .._utils import check_niimg, fill_doc
20
21_package_directory = os.path.dirname(os.path.abspath(__file__))
22MNI152_FILE_PATH = os.path.join(
23    _package_directory, "data",
24    "mni_icbm152_t1_tal_nlin_sym_09a_converted.nii.gz")
25GM_MNI152_FILE_PATH = os.path.join(
26    _package_directory, "data",
27    "mni_icbm152_gm_tal_nlin_sym_09a_converted.nii.gz")
28WM_MNI152_FILE_PATH = os.path.join(
29    _package_directory, "data",
30    "mni_icbm152_wm_tal_nlin_sym_09a_converted.nii.gz")
31FSAVERAGE5_PATH = os.path.join(_package_directory, "data", "fsaverage5")
32
33
34# workaround for
35# https://github.com/nilearn/nilearn/pull/2738#issuecomment-869018842
36_MNI_RES_WARNING_ALREADY_SHOWN = False
37
38
39@fill_doc
40def fetch_icbm152_2009(data_dir=None, url=None, resume=True, verbose=1):
41    """Download and load the ICBM152 template (dated 2009).
42
43    For more information, see :footcite:`FONOV2011313`,
44    :footcite:`Fonov2009`, and :footcite:`Collins1999algorithm`.
45
46    Parameters
47    ----------
48    %(data_dir)s
49    %(url)s
50    %(resume)s
51    %(verbose)s
52
53    Returns
54    -------
55    data : sklearn.datasets.base.Bunch
56        Dictionary-like object, interest keys are:
57
58        - "t1": str,
59          Path to T1-weighted anatomical image
60        - "t2": str,
61          Path to T2-weighted anatomical image
62        - "t2_relax": str,
63          Path to anatomical image obtained with the T2 relaxometry
64        - "pd": str,
65          Path to the proton density weighted anatomical image
66        - "gm": str,
67          Path to grey matter segmented image
68        - "wm": str,
69          Path to white matter segmented image
70        - "csf": str,
71          Path to cerebrospinal fluid segmented image
72        - "eye_mask": str,
73          Path to eye mask useful to mask out part of MRI images
74        - "face_mask": str,
75          Path to face mask useful to mask out part of MRI images
76        - "mask": str,
77          Path to whole brain mask useful to mask out skull areas
78
79    See Also
80    --------
81    nilearn.datasets.load_mni152_template: to load MNI152 T1 template.
82
83    nilearn.datasets.load_mni152_gm_template: to load MNI152 grey matter
84        template.
85
86    nilearn.datasets.load_mni152_wm_template: to load MNI152 white matter
87        template.
88
89    nilearn.datasets.load_mni152_brain_mask: to load MNI152 whole brain mask.
90
91    nilearn.datasets.load_mni152_gm_mask: to load MNI152 grey matter mask.
92
93    nilearn.datasets.load_mni152_wm_mask: to load MNI152 white matter mask.
94
95    nilearn.datasets.fetch_icbm152_brain_gm_mask: to fetch only ICBM grey
96        matter mask.
97
98    References
99    ----------
100    .. footbibliography::
101
102    Notes
103    -----
104    For more information about this dataset's structure:
105    http://www.bic.mni.mcgill.ca/ServicesAtlases/ICBM152NLin2009
106
107    The original download URL is
108    http://www.bic.mni.mcgill.ca/~vfonov/icbm/2009/mni_icbm152_nlin_sym_09a_nifti.zip
109
110    """
111    if url is None:
112        # The URL can be retrieved from the nilearn account on OSF (Open
113        # Science Framework), https://osf.io/4r3jt/quickfiles/
114        # Clicking on the "share" button gives the root of the URL.
115        url = "https://osf.io/7pj92/download"
116    opts = {'uncompress': True}
117
118    keys = ("csf", "gm", "wm",
119            "pd", "t1", "t2", "t2_relax",
120            "eye_mask", "face_mask", "mask")
121    filenames = [(os.path.join("mni_icbm152_nlin_sym_09a", name), url, opts)
122                 for name in (
123        "mni_icbm152_csf_tal_nlin_sym_09a.nii.gz",
124        "mni_icbm152_gm_tal_nlin_sym_09a.nii.gz",
125        "mni_icbm152_wm_tal_nlin_sym_09a.nii.gz",
126
127        "mni_icbm152_pd_tal_nlin_sym_09a.nii.gz",
128        "mni_icbm152_t1_tal_nlin_sym_09a.nii.gz",
129        "mni_icbm152_t2_tal_nlin_sym_09a.nii.gz",
130        "mni_icbm152_t2_relx_tal_nlin_sym_09a.nii.gz",
131
132        "mni_icbm152_t1_tal_nlin_sym_09a_eye_mask.nii.gz",
133        "mni_icbm152_t1_tal_nlin_sym_09a_face_mask.nii.gz",
134        "mni_icbm152_t1_tal_nlin_sym_09a_mask.nii.gz")]
135
136    dataset_name = 'icbm152_2009'
137    data_dir = _get_dataset_dir(dataset_name, data_dir=data_dir,
138                                verbose=verbose)
139    sub_files = _fetch_files(data_dir, filenames, resume=resume,
140                             verbose=verbose)
141
142    fdescr = _get_dataset_descr(dataset_name)
143
144    params = dict([('description', fdescr)] + list(zip(keys, sub_files)))
145    return Bunch(**params)
146
147
148@functools.lru_cache(maxsize=3)
149def load_mni152_template(resolution=None):
150    """Load the MNI152 skullstripped T1 template.
151    This function takes the skullstripped, re-scaled 1mm-resolution version of
152    the MNI ICBM152 T1 template and re-samples it using a different resolution,
153    if specified.
154
155    For more information, see :footcite:`FONOV2011313`,
156    and :footcite:`Fonov2009`.
157
158    Parameters
159    ----------
160    resolution: int, optional, Default = 2
161        If resolution is different from 1, the template is re-sampled with the
162        specified resolution.
163
164        .. versionadded:: 0.8.1
165
166    Returns
167    -------
168    mni152_template : Nifti1Image, image representing the re-sampled
169        whole-brain template
170
171    See Also
172    --------
173    nilearn.datasets.load_mni152_gm_template : for details about version of the
174        MNI152 grey-matter template.
175
176    nilearn.datasets.load_mni152_wm_template : for details about version of the
177        MNI152 white-matter template.
178
179    References
180    ----------
181    .. footbibliography::
182
183    """
184
185    global _MNI_RES_WARNING_ALREADY_SHOWN
186    if resolution is None:
187        if not _MNI_RES_WARNING_ALREADY_SHOWN:
188            warnings.warn("Default resolution of the MNI template will change "
189                          "from 2mm to 1mm in version 0.10.0", FutureWarning,
190                          stacklevel=2)
191            _MNI_RES_WARNING_ALREADY_SHOWN = True
192        resolution = 2
193
194    brain_template = check_niimg(MNI152_FILE_PATH)
195
196    # Typecasting
197    brain_data = get_data(brain_template).astype("float32")
198
199    # Re-scale template from 0 to 1
200    brain_data /= brain_data.max()
201    new_brain_template = new_img_like(brain_template, brain_data)
202
203    # Resample template according to the pre-specified resolution, if different
204    # than 1
205    if resolution != 1:
206        new_brain_template = resampling.resample_img(new_brain_template,
207                                                     np.eye(3) * resolution)
208
209    return new_brain_template
210
211
212def load_mni152_gm_template(resolution=None):
213    """Load the MNI152 grey-matter template.
214    This function takes the re-scaled 1mm-resolution version of the grey-matter
215    MNI ICBM152 template and re-samples it using a different resolution,
216    if specified.
217
218    .. versionadded:: 0.8.1
219
220    Parameters
221    ----------
222    resolution: int, optional, Default = 2
223        If resolution is different from 1, the template is re-sampled with the
224        specified resolution.
225
226    Returns
227    -------
228    gm_mni152_template : Nifti1Image, image representing the resampled
229        grey-matter template
230
231    See Also
232    --------
233    nilearn.datasets.load_mni152_template : for details about version of the
234        MNI152 T1 template.
235
236    nilearn.datasets.load_mni152_wm_template : for details about version of the
237        MNI152 white-matter template.
238
239    """
240
241    global _MNI_RES_WARNING_ALREADY_SHOWN
242    if resolution is None:
243        if not _MNI_RES_WARNING_ALREADY_SHOWN:
244            warnings.warn("Default resolution of the MNI template will change "
245                          "from 2mm to 1mm in version 0.10.0", FutureWarning)
246            _MNI_RES_WARNING_ALREADY_SHOWN = True
247        resolution = 2
248
249    gm_template = check_niimg(GM_MNI152_FILE_PATH)
250
251    # Typecasting
252    gm_data = get_data(gm_template).astype("float32")
253
254    # Re-scale template from 0 to 1
255    gm_data /= gm_data.max()
256    new_gm_template = new_img_like(gm_template, gm_data)
257
258    # Resample template according to the pre-specified resolution, if different
259    # than 1
260    if resolution != 1:
261        new_gm_template = resampling.resample_img(new_gm_template,
262                                                  np.eye(3) * resolution)
263
264    return new_gm_template
265
266
267def load_mni152_wm_template(resolution=None):
268    """Load the MNI152 white-matter template.
269    This function takes the re-scaled 1mm-resolution version of the
270    white-matter MNI ICBM152 template and re-samples it using a different
271    resolution, if specified.
272
273    .. versionadded:: 0.8.1
274
275    Parameters
276    ----------
277    resolution: int, optional, Default = 2
278        If resolution is different from 1, the template is re-sampled with the
279        specified resolution.
280
281    Returns
282    -------
283    wm_mni152_template : Nifti1Image, image representing the resampled
284        white-matter template
285
286    See Also
287    --------
288    nilearn.datasets.load_mni152_template : for details about version of the
289        MNI152 T1 template.
290
291    nilearn.datasets.load_mni152_gm_template : for details about version of the
292        MNI152 grey-matter template.
293
294    """
295
296    global _MNI_RES_WARNING_ALREADY_SHOWN
297    if resolution is None:
298        if not _MNI_RES_WARNING_ALREADY_SHOWN:
299            warnings.warn("Default resolution of the MNI template will change "
300                          "from 2mm to 1mm in version 0.10.0", FutureWarning)
301            _MNI_RES_WARNING_ALREADY_SHOWN = True
302        resolution = 2
303
304    wm_template = check_niimg(WM_MNI152_FILE_PATH)
305
306    # Typecasting
307    wm_data = get_data(wm_template).astype("float32")
308
309    # Re-scale template from 0 to 1
310    wm_data /= wm_data.max()
311    new_wm_template = new_img_like(wm_template, wm_data)
312
313    # Resample template according to the pre-specified resolution, if different
314    # than 1
315    if resolution != 1:
316        new_wm_template = resampling.resample_img(new_wm_template,
317                                                  np.eye(3) * resolution)
318
319    return new_wm_template
320
321
322def load_mni152_brain_mask(resolution=None, threshold=0.2):
323    """Load the MNI152 whole-brain mask.
324    This function takes the whole-brain MNI152 T1 template and threshold it,
325    in order to obtain the corresponding whole-brain mask.
326
327    .. versionadded:: 0.2.5
328
329    Parameters
330    ----------
331    resolution: int, optional, Default = 2
332        If resolution is different from 1, the template loaded is first
333        re-sampled with the specified resolution.
334
335        .. versionadded:: 0.8.1
336
337    threshold : float, optional
338        Values of the MNI152 T1 template above this threshold will be included.
339        Default=0.2
340
341    Returns
342    -------
343    mask_img : Nifti1Image, image corresponding to the whole-brain mask.
344
345    Notes
346    -----
347    Refer to load_mni152_template function for more information about the
348    MNI152 T1 template.
349
350    See Also
351    --------
352    nilearn.datasets.load_mni152_template : for details about version of the
353        MNI152 T1 template and related.
354
355    """
356
357    global _MNI_RES_WARNING_ALREADY_SHOWN
358    if resolution is None:
359        if not _MNI_RES_WARNING_ALREADY_SHOWN:
360            warnings.warn("Default resolution of the MNI template will change "
361                          "from 2mm to 1mm in version 0.10.0", FutureWarning)
362            _MNI_RES_WARNING_ALREADY_SHOWN = True
363        resolution = 2
364
365    # Load MNI template
366    target_img = load_mni152_template(resolution=resolution)
367    mask_voxels = (get_data(target_img) > threshold).astype("int8")
368    mask_img = new_img_like(target_img, mask_voxels)
369
370    return mask_img
371
372
373def load_mni152_gm_mask(resolution=None, threshold=0.2, n_iter=2):
374    """Load the MNI152 grey-matter mask.
375    This function takes the grey-matter MNI152 template and threshold it, in
376    order to obtain the corresponding grey-matter mask.
377
378    .. versionadded:: 0.8.1
379
380    Parameters
381    ----------
382    resolution: int, optional, Default = 2
383        If resolution is different from 1, the template loaded is first
384        re-sampled with the specified resolution.
385
386    threshold : float, optional
387        Values of the grey-matter MNI152 template above this threshold will be
388        included. Default=0.2
389
390    n_iter: int, optional, Default = 2
391        Number of repetitions of dilation and erosion steps performed in
392        scipy.ndimage.binary_closing function.
393
394    Returns
395    -------
396    gm_mask_img : Nifti1Image, image corresponding to the grey-matter mask.
397
398    Notes
399    -----
400    Refer to load_mni152_gm_template function for more information about the
401    MNI152 grey-matter template.
402
403    See Also
404    --------
405    nilearn.datasets.load_mni152_gm_template : for details about version of the
406        MNI152 grey-matter template and related.
407
408    """
409
410    global _MNI_RES_WARNING_ALREADY_SHOWN
411    if resolution is None:
412        if not _MNI_RES_WARNING_ALREADY_SHOWN:
413            warnings.warn("Default resolution of the MNI template will change "
414                          "from 2mm to 1mm in version 0.10.0", FutureWarning)
415            _MNI_RES_WARNING_ALREADY_SHOWN = True
416        resolution = 2
417
418    # Load MNI template
419    gm_target = load_mni152_gm_template(resolution=resolution)
420    gm_target_img = check_niimg(gm_target)
421    gm_target_data = get_data(gm_target_img)
422
423    gm_target_mask = (gm_target_data > threshold).astype("int8")
424
425    gm_target_mask = ndimage.binary_closing(gm_target_mask, iterations=n_iter)
426    gm_mask_img = new_img_like(gm_target_img, gm_target_mask)
427
428    return gm_mask_img
429
430
431def load_mni152_wm_mask(resolution=None, threshold=0.2, n_iter=2):
432    """Load the MNI152 white-matter mask.
433    This function takes the white-matter MNI152 template and threshold it, in
434    order to obtain the corresponding white-matter mask.
435
436    .. versionadded:: 0.8.1
437
438    Parameters
439    ----------
440    resolution: int, optional, Default = 2
441        If resolution is different from 1, the template loaded is first
442        re-sampled with the specified resolution.
443
444    threshold : float, optional
445        Values of the white-matter MNI152 template above this threshold will be
446        included. Default=0.2
447
448    n_iter: int, optional, Default = 2
449        Number of repetitions of dilation and erosion steps performed in
450        scipy.ndimage.binary_closing function.
451
452    Returns
453    -------
454    wm_mask_img : Nifti1Image, image corresponding to the white-matter mask.
455
456    Notes
457    -----
458    Refer to load_mni152_gm_template function for more information about the
459    MNI152 white-matter template.
460
461    See Also
462    --------
463    nilearn.datasets.load_mni152_wm_template : for details about version of the
464        MNI152 white-matter template and related.
465
466    """
467
468    global _MNI_RES_WARNING_ALREADY_SHOWN
469    if resolution is None:
470        if not _MNI_RES_WARNING_ALREADY_SHOWN:
471            warnings.warn("Default resolution of the MNI template will change "
472                          "from 2mm to 1mm in version 0.10.0", FutureWarning)
473            _MNI_RES_WARNING_ALREADY_SHOWN = True
474        resolution = 2
475
476    # Load MNI template
477    wm_target = load_mni152_wm_template(resolution=resolution)
478    wm_target_img = check_niimg(wm_target)
479    wm_target_data = get_data(wm_target_img)
480
481    wm_target_mask = (wm_target_data > threshold).astype("int8")
482
483    wm_target_mask = ndimage.binary_closing(wm_target_mask, iterations=n_iter)
484    wm_mask_img = new_img_like(wm_target_img, wm_target_mask)
485
486    return wm_mask_img
487
488
489@fill_doc
490def fetch_icbm152_brain_gm_mask(data_dir=None, threshold=0.2, resume=True,
491                                n_iter=2, verbose=1):
492    """Downloads ICBM152 template first, then loads the 'gm' mask.
493
494    .. versionadded:: 0.2.5
495
496    Parameters
497    ----------
498    %(data_dir)s
499    threshold : float, optional
500        Values of the ICBM152 grey-matter template above this threshold will be
501        included. Default=0.2
502
503    %(resume)s
504    n_iter: int, optional, Default = 2
505        Number of repetitions of dilation and erosion steps performed in
506        scipy.ndimage.binary_closing function.
507
508        .. versionadded:: 0.8.1
509
510    %(verbose)s
511
512    Returns
513    -------
514    gm_mask_img : Nifti1Image, image corresponding to the brain grey matter
515        from ICBM152 template.
516
517    Notes
518    -----
519    This function relies on ICBM152 templates where we particularly pick
520    grey matter template and threshold the template at .2 to take one fifth
521    of the values. Then, do a bit post processing such as binary closing
522    operation to more compact mask image.
523
524    .. note::
525        It is advised to check the mask image with your own data processing.
526
527    See Also
528    --------
529    nilearn.datasets.fetch_icbm152_2009: for details regarding the ICBM152
530        template.
531
532    nilearn.datasets.load_mni152_template: for details about version of MNI152
533        template and related.
534
535    """
536    # Fetching ICBM152 grey matter mask image
537    icbm = fetch_icbm152_2009(data_dir=data_dir, resume=resume,
538                              verbose=verbose)
539    gm = icbm['gm']
540    gm_img = check_niimg(gm)
541    gm_data = get_data(gm_img)
542
543    # getting one fifth of the values
544    gm_mask = (gm_data > threshold).astype("int8")
545
546    gm_mask = ndimage.binary_closing(gm_mask, iterations=n_iter)
547    gm_mask_img = new_img_like(gm_img, gm_mask)
548
549    return gm_mask_img
550
551
552@fill_doc
553def fetch_oasis_vbm(n_subjects=None, dartel_version=True, data_dir=None,
554                    url=None, resume=True, verbose=1):
555    """Download and load Oasis "cross-sectional MRI" dataset (416 subjects).
556
557    For more information, see :footcite:`OASISbrain`,
558    and :footcite:`Marcus2007OASIS`.
559
560    Parameters
561    ----------
562    n_subjects : int, optional
563        The number of subjects to load. If None is given, all the
564        subjects are used.
565
566    dartel_version : boolean, optional
567        Whether or not to use data normalized with DARTEL instead of standard
568        SPM8 normalization. Default=True.
569    %(data_dir)s
570    %(url)s
571    %(resume)s
572    %(verbose)s
573
574    Returns
575    -------
576    data : Bunch
577        Dictionary-like object, the interest attributes are :
578
579        - 'gray_matter_maps': string list
580          Paths to nifti gray matter density probability maps
581        - 'white_matter_maps' string list
582          Paths to nifti white matter density probability maps
583        - 'ext_vars': np.recarray
584          Data from the .csv file with information about selected subjects
585        - 'data_usage_agreement': string
586          Path to the .txt file containing the data usage agreement.
587
588    References
589    ----------
590    .. footbibliography::
591
592    Notes
593    -----
594    In the DARTEL version, original Oasis data have been preprocessed
595    with the following steps:
596
597      1. Dimension swapping (technically required for subsequent steps)
598      2. Brain Extraction
599      3. Segmentation with SPM8
600      4. Normalization using DARTEL algorithm
601      5. Modulation
602      6. Replacement of NaN values with 0 in gray/white matter density maps.
603      7. Resampling to reduce shape and make it correspond to the shape of
604         the non-DARTEL data (fetched with dartel_version=False).
605      8. Replacement of values < 1e-4 with zeros to reduce the file size.
606
607    In the non-DARTEL version, the following steps have been performed instead:
608
609      1. Dimension swapping (technically required for subsequent steps)
610      2. Brain Extraction
611      3. Segmentation and normalization to a template with SPM8
612      4. Modulation
613      5. Replacement of NaN values with 0 in gray/white matter density maps.
614
615    An archive containing the gray and white matter density probability maps
616    for the 416 available subjects is provided. Gross outliers are removed and
617    filtered by this data fetcher (DARTEL: 13 outliers; non-DARTEL: 1 outlier)
618    Externals variates (age, gender, estimated intracranial volume,
619    years of education, socioeconomic status, dementia score) are provided
620    in a CSV file that is a copy of the original Oasis CSV file. The current
621    downloader loads the CSV file and keeps only the lines corresponding to
622    the subjects that are actually demanded.
623
624    The Open Access Structural Imaging Series (OASIS) is a project
625    dedicated to making brain imaging data openly available to the public.
626    Using data available through the OASIS project requires agreeing with
627    the Data Usage Agreement that can be found at
628    http://www.oasis-brains.org/app/template/UsageAgreement.vm
629
630    """
631    # check number of subjects
632    if n_subjects is None:
633        n_subjects = 403 if dartel_version else 415
634    if dartel_version:  # DARTEL version has 13 identified outliers
635        if n_subjects > 403:
636            warnings.warn('Only 403 subjects are available in the '
637                          'DARTEL-normalized version of the dataset. '
638                          'All of them will be used instead of the wanted %d'
639                          % n_subjects)
640            n_subjects = 403
641    else:  # all subjects except one are available with non-DARTEL version
642        if n_subjects > 415:
643            warnings.warn('Only 415 subjects are available in the '
644                          'non-DARTEL-normalized version of the dataset. '
645                          'All of them will be used instead of the wanted %d'
646                          % n_subjects)
647            n_subjects = 415
648    if n_subjects < 1:
649        raise ValueError("Incorrect number of subjects (%d)" % n_subjects)
650
651    # pick the archive corresponding to preprocessings type
652    if url is None:
653        if dartel_version:
654            url_images = ('https://www.nitrc.org/frs/download.php/'
655                          '6364/archive_dartel.tgz?i_agree=1&download_now=1')
656        else:
657            url_images = ('https://www.nitrc.org/frs/download.php/'
658                          '6359/archive.tgz?i_agree=1&download_now=1')
659        # covariates and license are in separate files on NITRC
660        url_csv = ('https://www.nitrc.org/frs/download.php/'
661                   '6348/oasis_cross-sectional.csv?i_agree=1&download_now=1')
662        url_dua = ('https://www.nitrc.org/frs/download.php/'
663                   '6349/data_usage_agreement.txt?i_agree=1&download_now=1')
664    else:  # local URL used in tests
665        url_csv = url + "/oasis_cross-sectional.csv"
666        url_dua = url + "/data_usage_agreement.txt"
667        if dartel_version:
668            url_images = url + "/archive_dartel.tgz"
669        else:
670            url_images = url + "/archive.tgz"
671
672    opts = {'uncompress': True}
673
674    # missing subjects create shifts in subjects ids
675    missing_subjects = [8, 24, 36, 48, 89, 93, 100, 118, 128, 149, 154,
676                        171, 172, 175, 187, 194, 196, 215, 219, 225, 242,
677                        245, 248, 251, 252, 257, 276, 297, 306, 320, 324,
678                        334, 347, 360, 364, 391, 393, 412, 414, 427, 436]
679
680    if dartel_version:
681        # DARTEL produces outliers that are hidden by nilearn API
682        removed_outliers = [27, 57, 66, 83, 122, 157, 222, 269, 282, 287,
683                            309, 428]
684        missing_subjects = sorted(missing_subjects + removed_outliers)
685        file_names_gm = [
686            (os.path.join(
687                    "OAS1_%04d_MR1",
688                    "mwrc1OAS1_%04d_MR1_mpr_anon_fslswapdim_bet.nii.gz")
689             % (s, s),
690             url_images, opts)
691            for s in range(1, 457) if s not in missing_subjects][:n_subjects]
692        file_names_wm = [
693            (os.path.join(
694                    "OAS1_%04d_MR1",
695                    "mwrc2OAS1_%04d_MR1_mpr_anon_fslswapdim_bet.nii.gz")
696             % (s, s),
697             url_images, opts)
698            for s in range(1, 457) if s not in missing_subjects]
699    else:
700        # only one gross outlier produced, hidden by nilearn API
701        removed_outliers = [390]
702        missing_subjects = sorted(missing_subjects + removed_outliers)
703        file_names_gm = [
704            (os.path.join(
705                    "OAS1_%04d_MR1",
706                    "mwc1OAS1_%04d_MR1_mpr_anon_fslswapdim_bet.nii.gz")
707             % (s, s),
708             url_images, opts)
709            for s in range(1, 457) if s not in missing_subjects][:n_subjects]
710        file_names_wm = [
711            (os.path.join(
712                    "OAS1_%04d_MR1",
713                    "mwc2OAS1_%04d_MR1_mpr_anon_fslswapdim_bet.nii.gz")
714             % (s, s),
715             url_images, opts)
716            for s in range(1, 457) if s not in missing_subjects]
717    file_names_extvars = [("oasis_cross-sectional.csv", url_csv, {})]
718    file_names_dua = [("data_usage_agreement.txt", url_dua, {})]
719    # restrict to user-specified number of subjects
720    file_names_gm = file_names_gm[:n_subjects]
721    file_names_wm = file_names_wm[:n_subjects]
722
723    file_names = (file_names_gm + file_names_wm +
724                  file_names_extvars + file_names_dua)
725    dataset_name = 'oasis1'
726    data_dir = _get_dataset_dir(dataset_name, data_dir=data_dir,
727                                verbose=verbose)
728    files = _fetch_files(data_dir, file_names, resume=resume,
729                         verbose=verbose)
730
731    # Build Bunch
732    gm_maps = files[:n_subjects]
733    wm_maps = files[n_subjects:(2 * n_subjects)]
734    ext_vars_file = files[-2]
735    data_usage_agreement = files[-1]
736
737    # Keep CSV information only for selected subjects
738    csv_data = np.recfromcsv(ext_vars_file)
739    # Comparisons to recfromcsv data must be bytes.
740    actual_subjects_ids = [("OAS1" +
741                            str.split(os.path.basename(x),
742                                      "OAS1")[1][:9]).encode()
743                           for x in gm_maps]
744    subject_mask = np.asarray([subject_id in actual_subjects_ids
745                               for subject_id in csv_data['id']])
746    csv_data = csv_data[subject_mask]
747
748    fdescr = _get_dataset_descr(dataset_name)
749
750    return Bunch(
751        gray_matter_maps=gm_maps,
752        white_matter_maps=wm_maps,
753        ext_vars=csv_data,
754        data_usage_agreement=data_usage_agreement,
755        description=fdescr)
756
757
758@fill_doc
759def fetch_surf_fsaverage(mesh='fsaverage5', data_dir=None):
760    """Download a Freesurfer fsaverage surface.
761    File names are subject to change and only attribute names
762    are guaranteed to be stable across nilearn versions.
763    See :footcite:`Fischl1999neurons`.
764
765    Parameters
766    ----------
767    mesh : str, optional
768        Which mesh to fetch. Should be one of the following values:
769        %(fsaverage_options)s
770        Default='fsaverage5'.
771    %(data_dir)s
772
773    Returns
774    -------
775    data : sklearn.datasets.base.Bunch
776        Dictionary-like object, the interest attributes are :
777         - 'area_left': Gifti file, left hemisphere area data
778         - 'area_right': Gifti file, right hemisphere area data
779         - 'curv_left': Gifti file, left hemisphere curvature data
780         - 'curv_right': Gifti file, right hemisphere curvature data
781         - 'pial_left': Gifti file, left hemisphere pial surface mesh
782         - 'pial_right': Gifti file, right hemisphere pial surface mesh
783         - 'infl_left': Gifti file, left hemisphere inflated pial surface mesh
784         - 'infl_right': Gifti file, right hemisphere inflated pial
785                         surface mesh
786         - 'sphere_left': Gifti file, left hemisphere sphere surface mesh
787         - 'sphere_right': Gifti file, right hemisphere sphere surface mesh
788         - 'sulc_left': Gifti file, left hemisphere sulcal depth data
789         - 'sulc_right': Gifti file, right hemisphere sulcal depth data
790         - 'thick_left': Gifti file, left hemisphere cortical thickness data
791         - 'thick_right': Gifti file, right hemisphere cortical thickness data
792         - 'white_left': Gifti file, left hemisphere white surface mesh
793         - 'white_right': Gifti file, right hemisphere white surface mesh
794
795    References
796    ----------
797    .. footbibliography::
798
799    """
800    available_meshes = [
801        "fsaverage3", "fsaverage4", "fsaverage5", "fsaverage5_sphere",
802        "fsaverage6", "fsaverage7", "fsaverage",
803    ]
804
805    # Call a dataset loader depending on the value of mesh
806    if (
807        mesh == "fsaverage3"
808        or mesh == "fsaverage4"
809        or mesh == "fsaverage6"
810        or mesh == "fsaverage7"
811        or mesh == "fsaverage"
812    ):
813        # rename mesh to "fsaverage" to download it once
814        # regardless of whether mesh equals "fsaverage" or "fsaverage7"
815        if mesh == "fsaverage7":
816            mesh = "fsaverage"
817
818        return _fetch_surf_fsaverage(mesh, data_dir=data_dir)
819    elif mesh == "fsaverage5":
820        return _fetch_surf_fsaverage5()
821    elif mesh == "fsaverage5_sphere":
822        warnings.warn(
823            "mesh='fsaverage5_sphere' has been deprecated "
824            "and will be removed in v0.9.0.\n"
825            "fsaverage5 sphere coordinates can now be accessed through "
826            "attributes sphere_{left, right} using mesh='fsaverage5'"
827        )
828        return _fetch_surf_fsaverage5()
829    else:
830        raise ValueError(
831            "'mesh' should be one of {}; {!r} was provided".format(
832                available_meshes, mesh
833            )
834        )
835
836
837def _fetch_surf_fsaverage5():
838    """Helper function to ship fsaverage5 surfaces and sulcal information
839    with Nilearn.
840
841    The source of the data is coming from nitrc based on this PR #1016.
842    Manually downloaded gzipped and shipped with this function.
843
844    Shipping is done with Nilearn based on issue #1705.
845
846    """
847    data_dir = Path(FSAVERAGE5_PATH)
848
849    data = {
850        "{}_{}".format(part, hemi): str(
851            data_dir / "{}_{}.gii.gz".format(part, hemi)
852        )
853        for part in [
854            "area", "curv", "infl", "pial",
855            "sphere", "sulc", "thick", "white"
856        ]
857        for hemi in ["left", "right"]
858    }
859    data["description"] = _get_dataset_descr("fsaverage5")
860
861    return Bunch(**data)
862
863
864def _fetch_surf_fsaverage(dataset_name, data_dir=None):
865    """Helper function to ship fsaverage{3,4,6,7} meshes.
866
867    These meshes can be used for visualization purposes, but also to run
868    cortical surface-based searchlight decoding.
869
870    The source of the data is downloaded from OSF.
871    """
872    dataset_dir = _get_dataset_dir(dataset_name, data_dir=data_dir)
873    opts = {'uncompress': True}
874
875    url = {
876        "fsaverage3": "https://osf.io/asvjk/download",
877        "fsaverage4": "https://osf.io/x2j49/download",
878        "fsaverage6": "https://osf.io/um5ag/download",
879        "fsaverage": "https://osf.io/q7a5k/download",  # fsaverage7
880    }[dataset_name]
881
882    # List of attributes exposed by the dataset
883    dataset_attributes = [
884        "{}_{}".format(part, hemi)
885        for part in [
886            "area", "curv", "infl", "pial",
887            "sphere", "sulc", "thick", "white"
888        ]
889        for hemi in ["left", "right"]
890    ]
891
892    # Note that the file names match the attribute's
893    _fetch_files(
894        dataset_dir,
895        [
896            ("{}.gii.gz".format(attribute), url, opts)
897            for attribute in dataset_attributes
898        ]
899    )
900
901    result = {
902        attribute: os.path.join(dataset_dir, "{}.gii.gz".format(attribute))
903        for attribute in dataset_attributes
904    }
905    result["description"] = str(_get_dataset_descr(dataset_name))
906
907    return Bunch(**result)
908