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

1"""Test the datasets module.""" 

2 

3import itertools 

4import re 

5import xml.etree.ElementTree as ET 

6from pathlib import Path 

7 

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 

14 

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 

39 

40 

41def validate_atlas(atlas_data, check_type=True): 

42 """Validate content of the atlas bunch. 

43 

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) 

54 

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 

63 

64 

65def test_downloader(tmp_path, request_mocker): 

66 # Sandboxing test 

67 # =============== 

68 

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. 

72 

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. 

81 

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() 

89 

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") 

94 

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 ] 

102 

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" 

112 

113 # Downloading test 

114 # ================ 

115 

116 # Now, we use the regular downloading feature. This will override the dummy 

117 # file created before. 

118 

119 fetch_atlas_craddock_2012(data_dir=tmp_path) 

120 with dummy_file.open() as f: 

121 stuff = f.read() 

122 assert stuff == "" 

123 

124 

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") 

129 

130 

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() 

155 

156 

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) 

161 

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 

176 

177 

178@pytest.fixture 

179def fsl_fetcher(name): 

180 return ( 

181 fetch_atlas_juelich 

182 if name == "Juelich" 

183 else fetch_atlas_harvard_oxford 

184 ) 

185 

186 

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) 

197 

198 

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 

213 

214 

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 ) 

261 

262 validate_atlas(atlas_instance) 

263 _test_atlas_instance_should_match_data( 

264 atlas_instance, 

265 is_symm=is_symm or split, 

266 ) 

267 

268 # check for typo in label names 

269 for label in atlas_instance.labels: 

270 # no extra whitespace 

271 assert label.strip() == label 

272 

273 

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 

289 

290 bunch = fetch_atlas_craddock_2012( 

291 data_dir=tmp_path, 

292 verbose=0, 

293 homogeneity=homogeneity, 

294 grp_mean=grp_mean, 

295 ) 

296 

297 validate_atlas(bunch) 

298 assert bunch["maps"] == str(tmp_path / "craddock_2012" / expected) 

299 

300 assert request_mocker.url_count == 1 

301 

302 

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 

309 

310 bunch = fetch_atlas_craddock_2012(data_dir=tmp_path, verbose=0) 

311 

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 ] 

326 

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) 

330 

331 

332def test_fetch_atlas_smith_2009(tmp_path, request_mocker): 

333 bunch = fetch_atlas_smith_2009(data_dir=tmp_path, verbose=0, dimension=20) 

334 

335 validate_atlas(bunch) 

336 assert bunch["maps"] == str(tmp_path / "smith_2009" / "rsn20.nii.gz") 

337 

338 # Old code 

339 bunch = fetch_atlas_smith_2009(data_dir=tmp_path, verbose=0) 

340 

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 ] 

350 

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) 

354 

355 

356def test_fetch_coords_power_2011(): 

357 bunch = atlas.fetch_coords_power_2011() 

358 

359 assert len(bunch.rois) == 264 

360 

361 

362def test_fetch_coords_seitzman_2018(): 

363 bunch = atlas.fetch_coords_seitzman_2018() 

364 

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" 

373 

374 bunch = atlas.fetch_coords_seitzman_2018(ordered_regions=False) 

375 assert np.any(bunch.networks != np.sort(bunch.networks)) 

376 

377 

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) 

393 

394 

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`. 

398 

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 ) 

405 

406 validate_atlas(bunch) 

407 

408 assert request_mocker.url_count == 1 

409 

410 name = "_lateralized" if lateralized else "" 

411 

412 assert bunch["maps"] == str( 

413 tmp_path / "destrieux_2009" / f"destrieux2009_rois{name}.nii.gz" 

414 ) 

415 

416 

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) 

437 

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 

443 

444 

445def _generate_yeo_data(tmp_path): 

446 """Generate files for a dummy Yeo atlas. 

447 

448 So far it only generates 7 networks data. 

449 """ 

450 dataset_name = "yeo_2011" 

451 yeo_archive_root = "Yeo_JNeurophysiol11_MNI152" 

452 

453 mock_dir = tmp_path / dataset_name / yeo_archive_root 

454 mock_dir.mkdir(exist_ok=True, parents=True) 

455 

456 # only mock 7 networks for now 

457 n_roi = 7 

458 

459 to_archive = {} 

460 

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 

472 

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 

489 

490 return dict_to_archive(to_archive, archive_format="zip") 

491 

492 

493def test_fetch_atlas_yeo_2011(tmp_path, request_mocker): 

494 """Check fetcher for the Yeo atlas. 

495 

496 Mocks data for each deterministic atlas and their look up tables. 

497 """ 

498 yeo_data = _generate_yeo_data(tmp_path) 

499 

500 request_mocker.url_mapping["*Yeo_JNeurophysiol11_MNI152*"] = yeo_data 

501 

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) 

506 

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) 

514 

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 ) 

519 

520 validate_atlas(dataset) 

521 

522 

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) 

527 

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 ) 

534 

535 

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") 

561 

562 for res in resolutions: 

563 dataset = fetch_atlas_difumo( 

564 data_dir=tmp_path, dimension=dim, resolution_mm=res, verbose=0 

565 ) 

566 

567 validate_atlas(dataset) 

568 assert len(dataset.labels) == dim 

569 assert isinstance(dataset.maps, str) 

570 assert request_mocker.url_count == url_count 

571 

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 ) 

577 

578 

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}") 

585 

586 

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" 

612 

613 atlas_file = "AAL.nii" if version == "SPM12" else "ROI_MNI_V4.nii" 

614 

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) 

618 

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 

627 

628 dataset = fetch_atlas_aal(version=version, data_dir=tmp_path, verbose=0) 

629 

630 validate_atlas(dataset) 

631 assert isinstance(dataset.maps, str) 

632 assert isinstance(dataset.indices, list) 

633 assert request_mocker.url_count == 1 

634 

635 

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) 

641 

642 

643def test_fetch_atlas_basc_multiscale_2015(tmp_path): 

644 resolution = 7 

645 

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" 

649 

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) 

654 

655 # default version='sym', 

656 data_sym = fetch_atlas_basc_multiscale_2015( 

657 data_dir=tmp_path, verbose=0, resolution=resolution 

658 ) 

659 

660 validate_atlas(data_sym) 

661 assert data_sym["maps"] == str( 

662 tmp_path / dataset_name / name_sym / basename_sym 

663 ) 

664 

665 name_asym = "template_cambridge_basc_multiscale_nii_asym" 

666 basename_asym = "template_cambridge_basc_multiscale_asym_scale007.nii.gz" 

667 

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) 

672 

673 data_asym = fetch_atlas_basc_multiscale_2015( 

674 version="asym", verbose=0, data_dir=tmp_path, resolution=resolution 

675 ) 

676 

677 validate_atlas(data_asym) 

678 assert data_asym["maps"] == str( 

679 tmp_path / dataset_name / name_asym / basename_asym 

680 ) 

681 

682 

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 ) 

690 

691 

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 ) 

716 

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" 

720 

721 assert data_sym[key] == str( 

722 tmp_path / dataset_name / name_sym / basename_sym 

723 ) 

724 

725 name_asym = "template_cambridge_basc_multiscale_nii_asym" 

726 basename_asym = f"template_cambridge_basc_multiscale_asym_{key}.nii.gz" 

727 

728 assert data_asym[key] == str( 

729 tmp_path / dataset_name / name_asym / basename_asym 

730 ) 

731 

732 assert request_mocker.url_count == 2 

733 

734 

735def test_fetch_coords_dosenbach_2010(): 

736 bunch = atlas.fetch_coords_dosenbach_2010() 

737 

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)) 

742 

743 bunch = atlas.fetch_coords_dosenbach_2010(ordered_regions=False) 

744 

745 assert np.any(bunch.networks != np.sort(bunch.networks)) 

746 

747 

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") 

752 

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 ] 

758 

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 ) 

765 

766 

767def test_fetch_atlas_surf_destrieux(tmp_path): 

768 data_dir = tmp_path / "destrieux_surface" 

769 data_dir.mkdir() 

770 

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 ) 

779 

780 bunch = fetch_atlas_surf_destrieux(data_dir=tmp_path, verbose=0) 

781 

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) 

785 

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,) 

790 

791 

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) 

805 

806 

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) 

812 

813 validate_atlas(talairach) 

814 

815 assert_array_equal( 

816 get_data(talairach.maps).ravel(), level_values.T.ravel() 

817 ) 

818 assert_array_equal(talairach.labels, ["Background", "b", "a"]) 

819 

820 talairach = fetch_atlas_talairach("ba", data_dir=tmp_path) 

821 

822 assert_array_equal(get_data(talairach.maps).ravel(), level_values.ravel()) 

823 

824 with pytest.raises(ValueError): 

825 fetch_atlas_talairach("bad_level") 

826 

827 

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") 

838 

839 data = fetch_atlas_pauli_2017("deterministic", data_dir) 

840 

841 validate_atlas(data) 

842 assert len(data.labels) == 17 

843 

844 values = get_data(load(data.maps)) 

845 

846 assert len(np.unique(values)) == 17 

847 

848 data = fetch_atlas_pauli_2017("probabilistic", data_dir) 

849 

850 validate_atlas(data) 

851 assert load(data.maps).shape[-1] == 16 

852 

853 with pytest.raises(NotImplementedError): 

854 fetch_atlas_pauli_2017("junk for testing", data_dir) 

855 

856 

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") 

872 

873 with pytest.warns(DeprecationWarning, match='The parameter "version"'): 

874 data = fetch_atlas_pauli_2017( 

875 version="probabilistic", data_dir=data_dir 

876 ) 

877 

878 assert load(data.maps).shape[-1] == 16 

879 

880 with pytest.warns( 

881 DeprecationWarning, match="The possible values for atlas_type" 

882 ): 

883 data = fetch_atlas_pauli_2017("det", data_dir) 

884 

885 assert len(data.labels) == 17 

886 

887 with pytest.warns( 

888 DeprecationWarning, match="The possible values for atlas_type" 

889 ): 

890 data = fetch_atlas_pauli_2017("prob", data_dir) 

891 

892 assert load(data.maps).shape[-1] == 16 

893 

894 

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") 

901 

902 

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) 

913 

914 

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) 

922 

923 

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 

939 

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) 

954 

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 ) 

962 

963 validate_atlas(data) 

964 

965 assert isinstance(data.maps, str) 

966 

967 assert len(data.labels) == n_rois 

968 assert data.labels[0] == "Background" 

969 assert data.labels[1].startswith(f"{yeo_networks}Networks") 

970 

971 img = load(data.maps) 

972 

973 assert img.header.get_zooms()[0] == resolution_mm 

974 assert np.array_equal(np.unique(img.dataobj), np.arange(n_rois + 1)) 

975 

976 

977@pytest.fixture 

978def aal_xml(): 

979 atlas = ET.Element("atlas", version="2.0") 

980 

981 data = ET.SubElement(atlas, "data") 

982 

983 label1 = ET.SubElement(data, "label") 

984 ET.SubElement(label1, "index").text = "2001" 

985 ET.SubElement(label1, "name").text = "Precentral_L" 

986 

987 # Convert the XML tree to a string with proper encoding and declaration 

988 return ET.ElementTree(atlas) 

989 

990 

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) 

1000 

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) 

1005 

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 )