Coverage for nilearn/datasets/tests/test_atlas.py: 0%
426 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-16 12:32 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-16 12:32 +0200
1"""Test the datasets module."""
3import itertools
4import re
5import xml.etree.ElementTree as ET
6from pathlib import Path
8import numpy as np
9import pandas as pd
10import pytest
11from nibabel import Nifti1Header, Nifti1Image, freesurfer, load, nifti1
12from numpy.testing import assert_array_equal
13from sklearn.utils import Bunch
15from nilearn._utils import data_gen
16from nilearn._utils.testing import serialize_niimg
17from nilearn.conftest import _rng
18from nilearn.datasets import atlas
19from nilearn.datasets._utils import fetch_files
20from nilearn.datasets.atlas import (
21 fetch_atlas_aal,
22 fetch_atlas_allen_2011,
23 fetch_atlas_basc_multiscale_2015,
24 fetch_atlas_craddock_2012,
25 fetch_atlas_destrieux_2009,
26 fetch_atlas_difumo,
27 fetch_atlas_harvard_oxford,
28 fetch_atlas_juelich,
29 fetch_atlas_msdl,
30 fetch_atlas_pauli_2017,
31 fetch_atlas_schaefer_2018,
32 fetch_atlas_smith_2009,
33 fetch_atlas_surf_destrieux,
34 fetch_atlas_talairach,
35 fetch_atlas_yeo_2011,
36)
37from nilearn.datasets.tests._testing import check_type_fetcher, dict_to_archive
38from nilearn.image import get_data
41def validate_atlas(atlas_data, check_type=True):
42 """Validate content of the atlas bunch.
44 Atlas must:
45 - be a bunch
46 - with description, template and atlas_type attributes
47 - deterministic atlases must have:
48 - a labels attribute that is a list
49 - a lut attribute that is a pd.DataFrame
50 """
51 if check_type:
52 check_type_fetcher(atlas_data)
53 assert isinstance(atlas_data, Bunch)
55 assert atlas_data.template != ""
56 assert atlas_data.atlas_type in {"deterministic", "probabilistic"}
57 if atlas_data.atlas_type == "deterministic":
58 assert isinstance(atlas_data.labels, list)
59 assert all(isinstance(x, str) for x in atlas_data.labels)
60 assert isinstance(atlas_data.lut, pd.DataFrame)
61 if "fsaverage" not in atlas_data.template:
62 assert "Background" in atlas_data.labels
65def test_downloader(tmp_path, request_mocker):
66 # Sandboxing test
67 # ===============
69 # When nilearn downloads a file, everything is first downloaded in a
70 # temporary directory (sandbox) and moved to the "real" data directory if
71 # all files are present. In case of error, the sandbox is deleted.
73 # To test this feature, we do as follow:
74 # - create the data dir with a file that has a specific content
75 # - try to download the dataset but make it fail
76 # on purpose (by requesting a file that is not in the archive)
77 # - check that the previously created file is untouched :
78 # - if sandboxing is faulty, the file would be replaced
79 # by the file of the archive
80 # - if sandboxing works, the file must be untouched.
82 local_archive = (
83 Path(__file__).parent / "data" / "craddock_2011_parcellations.tar.gz"
84 )
85 url = "http://example.com/craddock_atlas"
86 request_mocker.url_mapping["*craddock*"] = local_archive
87 datasetdir = tmp_path / "craddock_2012"
88 datasetdir.mkdir()
90 # Create a dummy file. If sandboxing is successful, it won't be overwritten
91 dummy_file = datasetdir / "random_all.nii.gz"
92 with dummy_file.open("w") as f:
93 f.write("stuff")
95 opts = {"uncompress": True}
96 files = [
97 ("random_all.nii.gz", url, opts),
98 # The following file does not exists. It will cause an abortion of
99 # the fetching procedure
100 ("bald.nii.gz", url, opts),
101 ]
103 with pytest.raises(IOError):
104 fetch_files(
105 str(tmp_path / "craddock_2012"),
106 files,
107 verbose=0,
108 )
109 with dummy_file.open("r") as f:
110 stuff = f.read(5)
111 assert stuff == "stuff"
113 # Downloading test
114 # ================
116 # Now, we use the regular downloading feature. This will override the dummy
117 # file created before.
119 fetch_atlas_craddock_2012(data_dir=tmp_path)
120 with dummy_file.open() as f:
121 stuff = f.read()
122 assert stuff == ""
125def test_fetch_atlas_source():
126 # specify non-existing atlas source
127 with pytest.raises(ValueError, match="Atlas source"):
128 atlas._get_atlas_data_and_labels("new_source", "not_inside")
131def _write_sample_atlas_metadata(ho_dir, filename, is_symm):
132 with Path(ho_dir, f"{filename}.xml").open("w") as dm:
133 if not is_symm:
134 dm.write(
135 "<?xml version='1.0' encoding='us-ascii'?>\n"
136 "<data>\n"
137 '<label index="0" x="48" y="94" z="35">R1</label>\n'
138 '<label index="1" x="25" y="70" z="32">R2</label>\n'
139 '<label index="2" x="33" y="73" z="63">R3</label>\n'
140 "</data>"
141 )
142 else:
143 dm.write(
144 "<?xml version='1.0' encoding='us-ascii'?>\n"
145 "<data>\n"
146 '<label index="0" x="63" y="86" z="49">Left R1</label>\n'
147 '<label index="1" x="21" y="86" z="33">Right R1</label>\n'
148 '<label index="2" x="64" y="69" z="32">Left R2</label>\n'
149 '<label index="3" x="26" y="70" z="32">Right R2</label>\n'
150 '<label index="4" x="47" y="75" z="66">Left R3</label>\n'
151 '<label index="5" x="43" y="80" z="61">Right R3</label>\n'
152 "</data>"
153 )
154 dm.close()
157def _test_atlas_instance_should_match_data(atlas, is_symm):
158 assert Path(atlas.filename).exists() and Path(atlas.filename).is_absolute()
159 assert isinstance(atlas.maps, Nifti1Image)
160 assert isinstance(atlas.labels, list)
162 expected_atlas_labels = (
163 [
164 "Background",
165 "Left R1",
166 "Right R1",
167 "Left R2",
168 "Right R2",
169 "Left R3",
170 "Right R3",
171 ]
172 if is_symm
173 else ["Background", "R1", "R2", "R3"]
174 )
175 assert atlas.labels == expected_atlas_labels
178@pytest.fixture
179def fsl_fetcher(name):
180 return (
181 fetch_atlas_juelich
182 if name == "Juelich"
183 else fetch_atlas_harvard_oxford
184 )
187@pytest.mark.parametrize(
188 "name,prob", [("HarvardOxford", "cortl-prob-1mm"), ("Juelich", "prob-1mm")]
189)
190def test_fetch_atlas_fsl_errors(prob, fsl_fetcher, tmp_path):
191 # specify non-existing atlas item
192 with pytest.raises(ValueError, match="Invalid atlas name"):
193 fsl_fetcher("not_inside")
194 # Choose a probabilistic atlas with symmetric split
195 with pytest.raises(ValueError, match="Region splitting"):
196 fsl_fetcher(prob, data_dir=str(tmp_path), symmetric_split=True)
199@pytest.fixture
200def atlas_data():
201 # Create false atlas
202 atlas_data = np.zeros((10, 10, 10), dtype="int32")
203 # Create an interhemispheric map
204 atlas_data[:, :2, :] = 1
205 # Create a left map
206 atlas_data[5:, 7:9, :] = 3
207 atlas_data[5:, 3, :] = 2
208 # Create a right map, with one voxel on the left side
209 atlas_data[:5:, 3:5, :] = 2
210 atlas_data[:5, 8, :] = 3
211 atlas_data[4, 7, 0] = 3
212 return atlas_data
215@pytest.mark.parametrize(
216 "name,label_fname,fname,is_symm,split",
217 [
218 ("HarvardOxford", "-Cortical", "cort-prob-1mm", False, False),
219 ("HarvardOxford", "-Subcortical", "sub-maxprob-thr0-1mm", False, True),
220 (
221 "HarvardOxford",
222 "-Cortical-Lateralized",
223 "cortl-maxprob-thr0-1mm",
224 True,
225 True,
226 ),
227 ("Juelich", "", "prob-1mm", False, False),
228 ("Juelich", "", "maxprob-thr0-1mm", False, False),
229 ("Juelich", "", "maxprob-thr0-1mm", False, True),
230 ],
231)
232def test_fetch_atlas_fsl(
233 name,
234 label_fname,
235 fname,
236 is_symm,
237 split,
238 atlas_data,
239 fsl_fetcher,
240 tmp_path,
241 affine_eye,
242):
243 # Create directory which will contain fake atlas data
244 atlas_dir = tmp_path / "fsl" / "data" / "atlases"
245 nifti_dir = atlas_dir / name
246 nifti_dir.mkdir(parents=True)
247 # Write labels and sample atlas image in directory
248 _write_sample_atlas_metadata(
249 atlas_dir,
250 f"{name}{label_fname}",
251 is_symm=is_symm,
252 )
253 target_atlas_nii = nifti_dir / f"{name}-{fname}.nii.gz"
254 Nifti1Image(atlas_data, affine_eye * 3).to_filename(target_atlas_nii)
255 # Check that the fetch lead to consistent results
256 atlas_instance = fsl_fetcher(
257 fname,
258 data_dir=tmp_path,
259 symmetric_split=split,
260 )
262 validate_atlas(atlas_instance)
263 _test_atlas_instance_should_match_data(
264 atlas_instance,
265 is_symm=is_symm or split,
266 )
268 # check for typo in label names
269 for label in atlas_instance.labels:
270 # no extra whitespace
271 assert label.strip() == label
274@pytest.mark.parametrize(
275 "homogeneity, grp_mean, expected",
276 [
277 ("spatial", True, "scorr05_mean_all.nii.gz"),
278 ("random", True, "random_all.nii.gz"),
279 ("spatial", False, "scorr05_2level_all.nii.gz"),
280 ],
281)
282def test_fetch_atlas_craddock_2012(
283 tmp_path, request_mocker, homogeneity, grp_mean, expected
284):
285 local_archive = (
286 Path(__file__).parent / "data" / "craddock_2011_parcellations.tar.gz"
287 )
288 request_mocker.url_mapping["*craddock*"] = local_archive
290 bunch = fetch_atlas_craddock_2012(
291 data_dir=tmp_path,
292 verbose=0,
293 homogeneity=homogeneity,
294 grp_mean=grp_mean,
295 )
297 validate_atlas(bunch)
298 assert bunch["maps"] == str(tmp_path / "craddock_2012" / expected)
300 assert request_mocker.url_count == 1
303def test_fetch_atlas_craddock_2012_legacy(tmp_path, request_mocker):
304 """Check legacy format that return all maps at once."""
305 local_archive = (
306 Path(__file__).parent / "data" / "craddock_2011_parcellations.tar.gz"
307 )
308 request_mocker.url_mapping["*craddock*"] = local_archive
310 bunch = fetch_atlas_craddock_2012(data_dir=tmp_path, verbose=0)
312 keys = (
313 "scorr_mean",
314 "tcorr_mean",
315 "scorr_2level",
316 "tcorr_2level",
317 "random",
318 )
319 filenames = [
320 "scorr05_mean_all.nii.gz",
321 "tcorr05_mean_all.nii.gz",
322 "scorr05_2level_all.nii.gz",
323 "tcorr05_2level_all.nii.gz",
324 "random_all.nii.gz",
325 ]
327 assert request_mocker.url_count == 1
328 for key, fn in zip(keys, filenames):
329 assert bunch[key] == str(tmp_path / "craddock_2012" / fn)
332def test_fetch_atlas_smith_2009(tmp_path, request_mocker):
333 bunch = fetch_atlas_smith_2009(data_dir=tmp_path, verbose=0, dimension=20)
335 validate_atlas(bunch)
336 assert bunch["maps"] == str(tmp_path / "smith_2009" / "rsn20.nii.gz")
338 # Old code
339 bunch = fetch_atlas_smith_2009(data_dir=tmp_path, verbose=0)
341 keys = ("rsn20", "rsn10", "rsn70", "bm20", "bm10", "bm70")
342 filenames = [
343 "rsn20.nii.gz",
344 "PNAS_Smith09_rsn10.nii.gz",
345 "rsn70.nii.gz",
346 "bm20.nii.gz",
347 "PNAS_Smith09_bm10.nii.gz",
348 "bm70.nii.gz",
349 ]
351 assert request_mocker.url_count == 6
352 for key, fn in zip(keys, filenames):
353 assert bunch[key] == str(tmp_path / "smith_2009" / fn)
356def test_fetch_coords_power_2011():
357 bunch = atlas.fetch_coords_power_2011()
359 assert len(bunch.rois) == 264
362def test_fetch_coords_seitzman_2018():
363 bunch = atlas.fetch_coords_seitzman_2018()
365 assert len(bunch.rois) == 300
366 assert len(bunch.radius) == 300
367 assert len(bunch.networks) == 300
368 assert len(bunch.regions) == 300
369 assert len(np.unique(bunch.networks)) == 14
370 assert len(np.unique(bunch.regions)) == 8
371 assert_array_equal(bunch.networks, np.sort(bunch.networks))
372 assert bunch.regions[0] == "cortexL"
374 bunch = atlas.fetch_coords_seitzman_2018(ordered_regions=False)
375 assert np.any(bunch.networks != np.sort(bunch.networks))
378def _destrieux_data():
379 """Mock the download of the destrieux atlas."""
380 data = {"destrieux2009.rst": "readme"}
381 background_value = 0
382 atlas = _rng().integers(background_value, 10, (10, 10, 10), dtype="int32")
383 atlas_img = Nifti1Image(atlas, np.eye(4))
384 labels = "\n".join([f"{idx},label {idx}" for idx in range(10)])
385 labels = f"index,name\n0,Background\n{labels}"
386 for lat in ["_lateralized", ""]:
387 lat_data = {
388 f"destrieux2009_rois_labels{lat}.csv": labels,
389 f"destrieux2009_rois{lat}.nii.gz": atlas_img,
390 }
391 data.update(lat_data)
392 return dict_to_archive(data)
395@pytest.mark.parametrize("lateralized", [True, False])
396def test_fetch_atlas_destrieux_2009(tmp_path, request_mocker, lateralized):
397 """Tests for function `fetch_atlas_destrieux_2009`.
399 The atlas is fetched with different values for `lateralized`.
400 """
401 request_mocker.url_mapping["*destrieux2009.tgz"] = _destrieux_data()
402 bunch = fetch_atlas_destrieux_2009(
403 lateralized=lateralized, data_dir=tmp_path, verbose=0
404 )
406 validate_atlas(bunch)
408 assert request_mocker.url_count == 1
410 name = "_lateralized" if lateralized else ""
412 assert bunch["maps"] == str(
413 tmp_path / "destrieux_2009" / f"destrieux2009_rois{name}.nii.gz"
414 )
417def test_fetch_atlas_msdl(tmp_path, request_mocker):
418 labels = pd.DataFrame(
419 {
420 "x": [1.5, 1.2],
421 "y": [1.5, 1.3],
422 "z": [1.5, 1.4],
423 "name": ["Aud", "DMN"],
424 "net name": ["Aud", "DMN"],
425 }
426 )
427 root = Path("MSDL_rois")
428 archive = {
429 root / "msdl_rois_labels.csv": labels.to_csv(index=False),
430 root / "msdl_rois.nii": "",
431 root / "README.txt": "",
432 }
433 request_mocker.url_mapping["*MSDL_rois.zip"] = dict_to_archive(
434 archive, "zip"
435 )
436 dataset = fetch_atlas_msdl(data_dir=tmp_path, verbose=0)
438 validate_atlas(dataset)
439 assert isinstance(dataset.region_coords, list)
440 assert isinstance(dataset.networks, list)
441 assert isinstance(dataset.maps, str)
442 assert request_mocker.url_count == 1
445def _generate_yeo_data(tmp_path):
446 """Generate files for a dummy Yeo atlas.
448 So far it only generates 7 networks data.
449 """
450 dataset_name = "yeo_2011"
451 yeo_archive_root = "Yeo_JNeurophysiol11_MNI152"
453 mock_dir = tmp_path / dataset_name / yeo_archive_root
454 mock_dir.mkdir(exist_ok=True, parents=True)
456 # only mock 7 networks for now
457 n_roi = 7
459 to_archive = {}
461 mock_map = data_gen.generate_labeled_regions((53, 64, 52), n_roi)
462 for basename in (
463 "Yeo2011_7Networks_MNI152_FreeSurferConformed1mm.nii.gz",
464 "Yeo2011_7Networks_MNI152_FreeSurferConformed1mm_LiberalMask.nii.gz",
465 "Yeo2011_17Networks_MNI152_FreeSurferConformed1mm.nii.gz",
466 "Yeo2011_17Networks_MNI152_FreeSurferConformed1mm_LiberalMask.nii.gz",
467 "FSL_MNI152_FreeSurferConformed_1mm.nii.gz",
468 ):
469 mock_file = mock_dir / basename
470 mock_map.to_filename(mock_file)
471 to_archive[Path(yeo_archive_root) / basename] = mock_file
473 mock_lut = pd.DataFrame(
474 {
475 "name": [None] + [f"7Networks_{x}" for x in range(1, n_roi + 1)],
476 "r": [1] * (n_roi + 1),
477 "g": [1] * (n_roi + 1),
478 "b": [1] * (n_roi + 1),
479 "o": [0] * (n_roi + 1),
480 }
481 )
482 for basename in (
483 "Yeo2011_7Networks_ColorLUT.txt",
484 "Yeo2011_17Networks_ColorLUT.txt",
485 ):
486 mock_file = mock_dir / basename
487 mock_lut.to_csv(mock_file, sep=" ", header=False)
488 to_archive[Path(yeo_archive_root) / basename] = mock_file
490 return dict_to_archive(to_archive, archive_format="zip")
493def test_fetch_atlas_yeo_2011(tmp_path, request_mocker):
494 """Check fetcher for the Yeo atlas.
496 Mocks data for each deterministic atlas and their look up tables.
497 """
498 yeo_data = _generate_yeo_data(tmp_path)
500 request_mocker.url_mapping["*Yeo_JNeurophysiol11_MNI152*"] = yeo_data
502 with pytest.warns(
503 DeprecationWarning, match="the parameters 'n_networks' and 'thickness'"
504 ):
505 dataset = fetch_atlas_yeo_2011(data_dir=tmp_path, verbose=0)
507 assert isinstance(dataset.anat, str)
508 assert isinstance(dataset.colors_17, str)
509 assert isinstance(dataset.colors_7, str)
510 assert isinstance(dataset.thick_17, str)
511 assert isinstance(dataset.thick_7, str)
512 assert isinstance(dataset.thin_17, str)
513 assert isinstance(dataset.thin_7, str)
515 dataset = fetch_atlas_yeo_2011(data_dir=tmp_path, verbose=0, n_networks=7)
516 dataset = fetch_atlas_yeo_2011(
517 data_dir=tmp_path, verbose=0, thickness="thick"
518 )
520 validate_atlas(dataset)
523def test_fetch_atlas_yeo_2011_error(tmp_path):
524 """Raise errors when the wrong values are passed."""
525 with pytest.raises(ValueError, match="'n_networks' must be 7 or 17."):
526 fetch_atlas_yeo_2011(data_dir=tmp_path, verbose=0, n_networks=10)
528 with pytest.raises(
529 ValueError, match="'thickness' must be 'thin' or 'thick'."
530 ):
531 fetch_atlas_yeo_2011(
532 data_dir=tmp_path, verbose=0, thickness="dead_parot"
533 )
536def test_fetch_atlas_difumo(tmp_path, request_mocker):
537 resolutions = [2, 3] # Valid resolution values
538 dimensions = [64, 128, 256, 512, 1024] # Valid dimension values
539 dimension_urls = ["pqu9r", "wjvd5", "3vrct", "9b76y", "34792"]
540 url_mapping = dict(zip(dimensions, dimension_urls))
541 for url_count, dim in enumerate(dimensions, start=2):
542 url = f"*osf.io/{url_mapping[dim]}/*"
543 labels = pd.DataFrame(
544 {
545 "Component": list(range(1, dim + 1)),
546 "Difumo_names": ["" for _ in range(dim)],
547 "Yeo_networks7": ["" for _ in range(dim)],
548 "Yeo_networks17": ["" for _ in range(dim)],
549 "GM": ["" for _ in range(dim)],
550 "WM": ["" for _ in range(dim)],
551 "CSF": ["" for _ in range(dim)],
552 }
553 )
554 root = Path(f"{dim}")
555 archive = {
556 root / f"labels_{dim}_dictionary.csv": labels.to_csv(index=False),
557 root / "2mm" / "maps.nii.gz": "",
558 root / "3mm" / "maps.nii.gz": "",
559 }
560 request_mocker.url_mapping[url] = dict_to_archive(archive, "zip")
562 for res in resolutions:
563 dataset = fetch_atlas_difumo(
564 data_dir=tmp_path, dimension=dim, resolution_mm=res, verbose=0
565 )
567 validate_atlas(dataset)
568 assert len(dataset.labels) == dim
569 assert isinstance(dataset.maps, str)
570 assert request_mocker.url_count == url_count
572 with pytest.raises(ValueError):
573 fetch_atlas_difumo(data_dir=tmp_path, dimension=42, resolution_mm=3)
574 fetch_atlas_difumo(
575 data_dir=tmp_path, dimension=128, resolution_mm=3.14
576 )
579@pytest.fixture
580def aal_archive_root(version):
581 if version == "SPM12":
582 return Path("aal", "atlas")
583 else:
584 return Path(f"aal_for_{version}")
587@pytest.mark.parametrize(
588 "version,archive_format,url_key",
589 [
590 ("SPM5", "zip", "uploads"),
591 ("SPM8", "zip", "uploads"),
592 ("SPM12", "gztar", "AAL_files"),
593 ],
594)
595def test_fetch_atlas_aal(
596 version,
597 archive_format,
598 url_key,
599 aal_archive_root,
600 tmp_path,
601 request_mocker,
602 img_3d_rand_eye,
603):
604 metadata = "1\t2\t3\n"
605 if version == "SPM12":
606 metadata = (
607 b"<?xml version='1.0' encoding='us-ascii'?>"
608 b"<metadata><label><index>1</index>"
609 b"<name>A</name></label></metadata>"
610 )
611 label_file = "AAL.xml" if version == "SPM12" else "ROI_MNI_V4.txt"
613 atlas_file = "AAL.nii" if version == "SPM12" else "ROI_MNI_V4.nii"
615 mock_file = tmp_path / f"aal_{version}" / aal_archive_root / atlas_file
616 mock_file.parent.mkdir(exist_ok=True, parents=True)
617 img_3d_rand_eye.to_filename(mock_file)
619 aal_data = dict_to_archive(
620 {
621 aal_archive_root / label_file: metadata,
622 aal_archive_root / atlas_file: mock_file,
623 },
624 archive_format=archive_format,
625 )
626 request_mocker.url_mapping[f"*{url_key}*"] = aal_data
628 dataset = fetch_atlas_aal(version=version, data_dir=tmp_path, verbose=0)
630 validate_atlas(dataset)
631 assert isinstance(dataset.maps, str)
632 assert isinstance(dataset.indices, list)
633 assert request_mocker.url_count == 1
636def test_fetch_atlas_aal_version_error(tmp_path):
637 with pytest.raises(
638 ValueError, match="The version of AAL requested 'FLS33'"
639 ):
640 fetch_atlas_aal(version="FLS33", data_dir=tmp_path, verbose=0)
643def test_fetch_atlas_basc_multiscale_2015(tmp_path):
644 resolution = 7
646 dataset_name = "basc_multiscale_2015"
647 name_sym = "template_cambridge_basc_multiscale_nii_sym"
648 basename_sym = "template_cambridge_basc_multiscale_sym_scale007.nii.gz"
650 mock_map = data_gen.generate_labeled_regions((53, 64, 52), resolution)
651 mock_file = tmp_path / dataset_name / name_sym / basename_sym
652 mock_file.parent.mkdir(exist_ok=True, parents=True)
653 mock_map.to_filename(mock_file)
655 # default version='sym',
656 data_sym = fetch_atlas_basc_multiscale_2015(
657 data_dir=tmp_path, verbose=0, resolution=resolution
658 )
660 validate_atlas(data_sym)
661 assert data_sym["maps"] == str(
662 tmp_path / dataset_name / name_sym / basename_sym
663 )
665 name_asym = "template_cambridge_basc_multiscale_nii_asym"
666 basename_asym = "template_cambridge_basc_multiscale_asym_scale007.nii.gz"
668 # version='asym'
669 mock_file = tmp_path / dataset_name / name_asym / basename_asym
670 mock_file.parent.mkdir(exist_ok=True, parents=True)
671 mock_map.to_filename(mock_file)
673 data_asym = fetch_atlas_basc_multiscale_2015(
674 version="asym", verbose=0, data_dir=tmp_path, resolution=resolution
675 )
677 validate_atlas(data_asym)
678 assert data_asym["maps"] == str(
679 tmp_path / dataset_name / name_asym / basename_asym
680 )
683def test_fetch_atlas_basc_multiscale_2015_error(tmp_path):
684 with pytest.raises(
685 ValueError, match="The version of Brain parcellations requested 'aym'"
686 ):
687 fetch_atlas_basc_multiscale_2015(
688 version="aym", data_dir=tmp_path, verbose=0
689 )
692@pytest.mark.parametrize(
693 "key",
694 [
695 "scale007",
696 "scale012",
697 "scale020",
698 "scale036",
699 "scale064",
700 "scale122",
701 "scale197",
702 "scale325",
703 "scale444",
704 ],
705)
706def test_fetch_atlas_basc_multiscale_2015_old_code(
707 key, tmp_path, request_mocker
708):
709 # Old code
710 # default version='sym',
711 data_sym = fetch_atlas_basc_multiscale_2015(data_dir=tmp_path, verbose=0)
712 # version='asym'
713 data_asym = fetch_atlas_basc_multiscale_2015(
714 version="asym", verbose=0, data_dir=tmp_path
715 )
717 dataset_name = "basc_multiscale_2015"
718 name_sym = "template_cambridge_basc_multiscale_nii_sym"
719 basename_sym = f"template_cambridge_basc_multiscale_sym_{key}.nii.gz"
721 assert data_sym[key] == str(
722 tmp_path / dataset_name / name_sym / basename_sym
723 )
725 name_asym = "template_cambridge_basc_multiscale_nii_asym"
726 basename_asym = f"template_cambridge_basc_multiscale_asym_{key}.nii.gz"
728 assert data_asym[key] == str(
729 tmp_path / dataset_name / name_asym / basename_asym
730 )
732 assert request_mocker.url_count == 2
735def test_fetch_coords_dosenbach_2010():
736 bunch = atlas.fetch_coords_dosenbach_2010()
738 assert len(bunch.rois) == 160
739 assert len(bunch.labels) == 160
740 assert len(np.unique(bunch.networks)) == 6
741 assert_array_equal(bunch.networks, np.sort(bunch.networks))
743 bunch = atlas.fetch_coords_dosenbach_2010(ordered_regions=False)
745 assert np.any(bunch.networks != np.sort(bunch.networks))
748def test_fetch_atlas_allen_2011(tmp_path, request_mocker):
749 """Fetch allen atlas and checks filenames are those expected."""
750 bunch = fetch_atlas_allen_2011(data_dir=tmp_path, verbose=0)
751 keys = ("maps", "rsn28", "comps")
753 filenames = [
754 "ALL_HC_unthresholded_tmaps.nii.gz",
755 "RSN_HC_unthresholded_tmaps.nii.gz",
756 "rest_hcp_agg__component_ica_.nii.gz",
757 ]
759 validate_atlas(bunch)
760 assert request_mocker.url_count == 1
761 for key, fn in zip(keys, filenames):
762 assert bunch[key] == str(
763 tmp_path / "allen_rsn_2011" / "allen_rsn_2011" / fn
764 )
767def test_fetch_atlas_surf_destrieux(tmp_path):
768 data_dir = tmp_path / "destrieux_surface"
769 data_dir.mkdir()
771 # Create mock annots
772 for hemi in ("left", "right"):
773 freesurfer.write_annot(
774 data_dir / f"{hemi}.aparc.a2009s.annot",
775 np.arange(4),
776 np.zeros((4, 5)),
777 5 * ["a"],
778 )
780 bunch = fetch_atlas_surf_destrieux(data_dir=tmp_path, verbose=0)
782 # one exception is made here to return some numpy array
783 # so we do not check the type
784 validate_atlas(bunch, check_type=False)
786 # Our mock annots have 4 labels
787 assert len(bunch.labels) == 4
788 assert bunch.map_left.shape == (4,)
789 assert bunch.map_right.shape == (4,)
792def _get_small_fake_talairach():
793 labels = ["*", "b", "a"]
794 all_labels = itertools.product(*(labels,) * 5)
795 labels_txt = "\n".join(map(".".join, all_labels))
796 extensions = nifti1.Nifti1Extensions(
797 [nifti1.Nifti1Extension("afni", labels_txt.encode("utf-8"))]
798 )
799 img = Nifti1Image(
800 np.arange(243, dtype="int32").reshape((3, 9, 9)),
801 np.eye(4),
802 Nifti1Header(extensions=extensions),
803 )
804 return serialize_niimg(img, gzipped=False)
807@pytest.mark.timeout(0)
808def test_fetch_atlas_talairach(tmp_path, request_mocker):
809 request_mocker.url_mapping["*talairach.nii"] = _get_small_fake_talairach()
810 level_values = np.ones((81, 3)) * [0, 1, 2]
811 talairach = fetch_atlas_talairach("hemisphere", data_dir=tmp_path)
813 validate_atlas(talairach)
815 assert_array_equal(
816 get_data(talairach.maps).ravel(), level_values.T.ravel()
817 )
818 assert_array_equal(talairach.labels, ["Background", "b", "a"])
820 talairach = fetch_atlas_talairach("ba", data_dir=tmp_path)
822 assert_array_equal(get_data(talairach.maps).ravel(), level_values.ravel())
824 with pytest.raises(ValueError):
825 fetch_atlas_talairach("bad_level")
828def test_fetch_atlas_pauli_2017(tmp_path, request_mocker):
829 labels = pd.DataFrame({"label": [f"label_{i}" for i in range(16)]}).to_csv(
830 sep="\t", header=False
831 )
832 det_atlas = data_gen.generate_labeled_regions((7, 6, 5), 16)
833 prob_atlas, _ = data_gen.generate_maps((7, 6, 5), 16)
834 request_mocker.url_mapping["*osf.io/6qrcb/*"] = labels
835 request_mocker.url_mapping["*osf.io/5mqfx/*"] = det_atlas
836 request_mocker.url_mapping["*osf.io/w8zq2/*"] = prob_atlas
837 data_dir = str(tmp_path / "pauli_2017")
839 data = fetch_atlas_pauli_2017("deterministic", data_dir)
841 validate_atlas(data)
842 assert len(data.labels) == 17
844 values = get_data(load(data.maps))
846 assert len(np.unique(values)) == 17
848 data = fetch_atlas_pauli_2017("probabilistic", data_dir)
850 validate_atlas(data)
851 assert load(data.maps).shape[-1] == 16
853 with pytest.raises(NotImplementedError):
854 fetch_atlas_pauli_2017("junk for testing", data_dir)
857# TODO: remove this test after release 0.13.0
858def test_fetch_atlas_pauli_2017_deprecated_values(tmp_path, request_mocker):
859 """Tests nilearn.datasets.atlas.fetch_atlas_pauli_2017 to receive
860 DepricationWarning upon use of deprecated version parameter and its
861 possible values "prob" and "det".
862 """
863 labels = pd.DataFrame({"label": [f"label_{i}" for i in range(16)]}).to_csv(
864 sep="\t", header=False
865 )
866 det_atlas = data_gen.generate_labeled_regions((7, 6, 5), 16)
867 prob_atlas, _ = data_gen.generate_maps((7, 6, 5), 16)
868 request_mocker.url_mapping["*osf.io/6qrcb/*"] = labels
869 request_mocker.url_mapping["*osf.io/5mqfx/*"] = det_atlas
870 request_mocker.url_mapping["*osf.io/w8zq2/*"] = prob_atlas
871 data_dir = str(tmp_path / "pauli_2017")
873 with pytest.warns(DeprecationWarning, match='The parameter "version"'):
874 data = fetch_atlas_pauli_2017(
875 version="probabilistic", data_dir=data_dir
876 )
878 assert load(data.maps).shape[-1] == 16
880 with pytest.warns(
881 DeprecationWarning, match="The possible values for atlas_type"
882 ):
883 data = fetch_atlas_pauli_2017("det", data_dir)
885 assert len(data.labels) == 17
887 with pytest.warns(
888 DeprecationWarning, match="The possible values for atlas_type"
889 ):
890 data = fetch_atlas_pauli_2017("prob", data_dir)
892 assert load(data.maps).shape[-1] == 16
895def _schaefer_labels(match, requests): # noqa: ARG001
896 # fails if requests is not passed
897 info = match.groupdict()
898 label_names = [f"{info['network']}Networks"] * int(info["n_rois"])
899 labels = pd.DataFrame({"label": label_names})
900 return labels.to_csv(sep="\t", header=False).encode("utf-8")
903def _schaefer_img(match, requests): # noqa: ARG001
904 # fails if requests is not passed
905 info = match.groupdict()
906 shape = (15, 14, 13)
907 affine = np.eye(4) * float(info["res"])
908 affine[3, 3] = 1.0
909 img = data_gen.generate_labeled_regions(
910 shape, int(info["n_rois"]), affine=affine
911 )
912 return serialize_niimg(img)
915def test_fetch_atlas_schaefer_2018_errors():
916 with pytest.raises(ValueError):
917 fetch_atlas_schaefer_2018(n_rois=44)
918 with pytest.raises(ValueError):
919 fetch_atlas_schaefer_2018(yeo_networks=10)
920 with pytest.raises(ValueError):
921 fetch_atlas_schaefer_2018(resolution_mm=3)
924@pytest.mark.parametrize("n_rois", list(range(100, 1100, 100)))
925@pytest.mark.parametrize("yeo_networks", [7, 17])
926@pytest.mark.parametrize("resolution_mm", [1, 2])
927def test_fetch_atlas_schaefer_2018(
928 tmp_path, request_mocker, n_rois, yeo_networks, resolution_mm
929):
930 labels_pattern = re.compile(
931 r".*2018_(?P<n_rois>\d+)Parcels_(?P<network>\d+)Networks_order.txt"
932 )
933 img_pattern = re.compile(
934 r".*_(?P<n_rois>\d+)Parcels_(?P<network>\d+)"
935 r"Networks_order_FSLMNI152_(?P<res>\d)mm.nii.gz"
936 )
937 request_mocker.url_mapping[labels_pattern] = _schaefer_labels
938 request_mocker.url_mapping[img_pattern] = _schaefer_img
940 mock_lut = pd.DataFrame(
941 {
942 "name": [f"{yeo_networks}Networks_{x}" for x in range(1, n_rois)],
943 "r": [1] * (n_rois - 1),
944 "g": [1] * (n_rois - 1),
945 "b": [1] * (n_rois - 1),
946 "o": [0] * (n_rois - 1),
947 }
948 )
949 basename = f"Schaefer2018_{n_rois}Parcels_{yeo_networks}Networks_order.txt"
950 mock_dir = tmp_path / "schaefer_2018"
951 mock_dir.mkdir(exist_ok=True, parents=True)
952 mock_file = mock_dir / basename
953 mock_lut.to_csv(mock_file, sep="\t", header=False)
955 data = fetch_atlas_schaefer_2018(
956 n_rois=n_rois,
957 yeo_networks=yeo_networks,
958 resolution_mm=resolution_mm,
959 data_dir=tmp_path,
960 verbose=0,
961 )
963 validate_atlas(data)
965 assert isinstance(data.maps, str)
967 assert len(data.labels) == n_rois
968 assert data.labels[0] == "Background"
969 assert data.labels[1].startswith(f"{yeo_networks}Networks")
971 img = load(data.maps)
973 assert img.header.get_zooms()[0] == resolution_mm
974 assert np.array_equal(np.unique(img.dataobj), np.arange(n_rois + 1))
977@pytest.fixture
978def aal_xml():
979 atlas = ET.Element("atlas", version="2.0")
981 data = ET.SubElement(atlas, "data")
983 label1 = ET.SubElement(data, "label")
984 ET.SubElement(label1, "index").text = "2001"
985 ET.SubElement(label1, "name").text = "Precentral_L"
987 # Convert the XML tree to a string with proper encoding and declaration
988 return ET.ElementTree(atlas)
991def test_aal_version_deprecation(
992 tmp_path, shape_3d_default, affine_eye, aal_xml
993):
994 img = data_gen.generate_labeled_regions(
995 shape_3d_default, 15, affine=affine_eye
996 )
997 output_path = tmp_path / "aal_SPM12/aal/atlas/AAL.nii"
998 output_path.parent.mkdir(parents=True)
999 img.to_filename(output_path)
1001 with (tmp_path / "aal_SPM12" / "aal" / "atlas" / "AAL.xml").open(
1002 "wb"
1003 ) as file:
1004 aal_xml.write(file, encoding="ISO-8859-1", xml_declaration=True)
1006 with pytest.deprecated_call(
1007 match=r"Starting in version 0\.13, the default fetched mask"
1008 ):
1009 fetch_atlas_aal(
1010 data_dir=tmp_path,
1011 )