Coverage for nilearn/image/tests/test_image.py: 0%

797 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-16 12:32 +0200

1"""Test image pre-processing functions.""" 

2 

3import platform 

4import warnings 

5from pathlib import Path 

6 

7import joblib 

8import numpy as np 

9import pandas as pd 

10import pytest 

11from nibabel import AnalyzeImage, Nifti1Image, Nifti2Image, load, spatialimages 

12from nibabel.freesurfer import MGHImage 

13from numpy.testing import ( 

14 assert_allclose, 

15 assert_almost_equal, 

16 assert_array_almost_equal, 

17 assert_array_equal, 

18 assert_equal, 

19) 

20 

21from nilearn import signal 

22from nilearn._utils import testing 

23from nilearn._utils.data_gen import ( 

24 _basic_confounds, 

25 generate_fake_fmri, 

26 generate_labeled_regions, 

27 generate_maps, 

28) 

29from nilearn._utils.exceptions import DimensionError 

30from nilearn.conftest import _affine_eye, _img_3d_rand, _rng, _shape_4d_default 

31from nilearn.image import ( 

32 binarize_img, 

33 clean_img, 

34 concat_imgs, 

35 copy_img, 

36 crop_img, 

37 get_data, 

38 high_variance_confounds, 

39 index_img, 

40 iter_img, 

41 largest_connected_component_img, 

42 math_img, 

43 mean_img, 

44 new_img_like, 

45 smooth_img, 

46 swap_img_hemispheres, 

47 threshold_img, 

48) 

49from nilearn.image.image import ( 

50 _crop_img_to, 

51 _fast_smooth_array, 

52 smooth_array, 

53) 

54from nilearn.image.resampling import resample_img 

55from nilearn.image.tests._testing import match_headers_keys 

56from nilearn.surface.surface import SurfaceImage 

57from nilearn.surface.surface import get_data as get_surface_data 

58from nilearn.surface.utils import ( 

59 assert_polymesh_equal, 

60 assert_surface_image_equal, 

61) 

62 

63X64 = platform.architecture()[0] == "64bit" 

64 

65 

66AFFINE_TO_TEST = [ 

67 _affine_eye(), 

68 np.diag((1, 1, -1, 1)), 

69 np.diag((0.6, 1, 0.6, 1)), 

70] 

71 

72NON_EYE_AFFINE = np.array( 

73 [ 

74 [1.0, 2.0, 3.0, 4.0], 

75 [5.0, 6.0, 7.0, 8.0], 

76 [9.0, 10.0, 11.0, 12.0], 

77 [0.0, 0.0, 0.0, 1.0], 

78 ] 

79) 

80 

81 

82def _new_data_for_smooth_array(): 

83 # Impulse in 3D 

84 data = np.zeros((40, 41, 42)) 

85 data[20, 20, 20] = 1 

86 return data 

87 

88 

89def _make_largest_cc_img_test_data(): 

90 shapes = ((10, 11, 12), (13, 14, 15)) 

91 regions = [1, 3] 

92 

93 img1 = generate_labeled_regions(shape=shapes[0], n_regions=regions[0]) 

94 img2 = generate_labeled_regions(shape=shapes[1], n_regions=regions[1]) 

95 return img1, img2, shapes 

96 

97 

98def _images_to_mean(): 

99 """Return a mixture of 4D and 3D images.""" 

100 rng = _rng() 

101 

102 data1 = np.zeros((5, 6, 7)) 

103 data2 = rng.uniform(size=(5, 6, 7)) 

104 data3 = rng.uniform(size=(5, 6, 7, 3)) 

105 

106 affine = np.diag((4, 3, 2, 1)) 

107 

108 img1 = Nifti1Image(data1, affine=affine) 

109 img2 = Nifti1Image(data2, affine=affine) 

110 img3 = Nifti1Image(data3, affine=affine) 

111 

112 imgs = ( 

113 [img1], 

114 [img1, img2], 

115 [img2, img1, img2], 

116 [img3, img1, img2], 

117 ) 

118 return imgs 

119 

120 

121def _check_fwhm(data, affine, fwhm): 

122 """Expect a full-width at half maximum of fwhm / voxel_size.""" 

123 vmax = data.max() 

124 above_half_max = data > 0.5 * vmax 

125 for axis in [0, 1, 2]: 

126 proj = np.any( 

127 np.any(np.rollaxis(above_half_max, axis=axis), axis=-1), 

128 axis=-1, 

129 ) 

130 assert_equal(proj.sum(), fwhm / np.abs(affine[axis, axis])) 

131 

132 

133def _mean_ground_truth(imgs): 

134 arrays = [] 

135 for img in imgs: 

136 img = get_data(img) 

137 if img.ndim == 4: 

138 img = np.mean(img, axis=-1) 

139 arrays.append(img) 

140 return np.mean(arrays, axis=0) 

141 

142 

143@pytest.fixture(scope="session") 

144def smooth_array_data(): 

145 return _new_data_for_smooth_array() 

146 

147 

148@pytest.fixture(scope="session") 

149def stat_img_test_data(): 

150 shape = (20, 20, 30) 

151 affine = _affine_eye() 

152 data = np.zeros(shape, dtype="int32") 

153 data[:2, :2, :2] = 4 # 8-voxel positive cluster 

154 data[4:6, :2, :2] = -4 # 8-voxel negative cluster 

155 data[8:11, 0, 0] = 5 # 3-voxel positive cluster 

156 data[13:16, 0, 0] = -5 # 3-voxel positive cluster 

157 data[:6, 4:10, :6] = 1 # 216-voxel positive cluster with low value 

158 data[13:19, 4:10, :6] = -1 # 216-voxel negative cluster with low value 

159 

160 stat_img = Nifti1Image(data, affine) 

161 

162 return stat_img 

163 

164 

165def test_get_data(tmp_path, shape_3d_default): 

166 img, *_ = generate_fake_fmri(shape=shape_3d_default) 

167 

168 data = get_data(img) 

169 

170 assert data.shape == img.shape 

171 assert data is img._data_cache 

172 

173 mask_img = new_img_like(img, data > 0) 

174 data = get_data(mask_img) 

175 

176 assert data.dtype == np.dtype("uint8") 

177 

178 img_3d = index_img(img, 0) 

179 

180 filename = str(tmp_path / "img_{}.nii.gz") 

181 img_3d.to_filename(filename.format("a")) 

182 img_3d.to_filename(filename.format("b")) 

183 

184 data = get_data(filename.format("a")) 

185 

186 assert len(data.shape) == 3 

187 

188 data = get_data(filename.format("*")) 

189 

190 assert len(data.shape) == 4 

191 

192 

193def test_high_variance_confounds(shape_3d_default): 

194 """Check high_variance_confounds returns proper shape. 

195 

196 See also test_signals.test_high_variance_confounds() 

197 There is only tests on what is added by high_variance_confounds() 

198 compared to signal.high_variance_confounds() 

199 """ 

200 length = 17 

201 n_confounds = 10 

202 

203 img, mask_img = generate_fake_fmri(shape=shape_3d_default, length=length) 

204 

205 confounds1 = high_variance_confounds( 

206 img, mask_img=mask_img, percentile=10.0, n_confounds=n_confounds 

207 ) 

208 

209 assert confounds1.shape == (length, n_confounds) 

210 

211 # No mask. 

212 confounds2 = high_variance_confounds( 

213 img, percentile=10.0, n_confounds=n_confounds 

214 ) 

215 

216 assert confounds2.shape == (length, n_confounds) 

217 

218 

219def test_high_variance_confounds_surface(surf_mask_1d, surface_glm_data): 

220 """Check high_variance_confounds returns proper shape from surface.""" 

221 length = 17 

222 n_confounds = 10 

223 

224 img, _ = surface_glm_data(length) 

225 

226 confounds1 = high_variance_confounds( 

227 img, mask_img=surf_mask_1d, percentile=10.0, n_confounds=n_confounds 

228 ) 

229 

230 assert confounds1.shape == (length, n_confounds) 

231 

232 # No mask. 

233 confounds2 = high_variance_confounds( 

234 img, percentile=10.0, n_confounds=n_confounds 

235 ) 

236 

237 assert confounds2.shape == (length, n_confounds) 

238 

239 

240def test_fast_smooth_array(): 

241 N = 4 

242 shape = (N, N, N) 

243 # hardcoded in _fast_smooth_array 

244 neighbor_weight = 0.2 

245 # 6 neighbors in 3D if you are not on an edge 

246 n_neighbors_max = 6 

247 

248 data = np.ones(shape) 

249 smooth_data = _fast_smooth_array(data) 

250 

251 # this contains the number of neighbors for each cell in the array 

252 n_neighbors_arr = np.empty(shape) 

253 for (i, j, k), __ in np.ndenumerate(n_neighbors_arr): 

254 n_neighbors_arr[i, j, k] = ( 

255 3 + (0 < i < N - 1) + (0 < j < N - 1) + (0 < k < N - 1) 

256 ) 

257 

258 expected = (1 + neighbor_weight * n_neighbors_arr) / ( 

259 1 + neighbor_weight * n_neighbors_max 

260 ) 

261 assert_allclose(smooth_data, expected) 

262 

263 

264@pytest.mark.parametrize("affine", AFFINE_TO_TEST) 

265def test_smooth_array_fwhm_is_odd_with_copy(smooth_array_data, affine): 

266 """Test that fwhm divided by any affine is odd. 

267 

268 Otherwise assertion below will fail. 

269 ( 9 / 0.6 = 15 is fine) 

270 """ 

271 data = smooth_array_data 

272 fwhm = 9 

273 

274 filtered = smooth_array(data, affine, fwhm=fwhm, copy=True) 

275 

276 assert not np.may_share_memory(filtered, data) 

277 

278 _check_fwhm(filtered, affine, fwhm) 

279 

280 

281@pytest.mark.parametrize("affine", AFFINE_TO_TEST) 

282def test_smooth_array_fwhm_is_odd_no_copy(affine): 

283 """Test that fwhm divided by any affine is odd. 

284 

285 Otherwise assertion below will fail. 

286 ( 9 / 0.6 = 15 is fine) 

287 """ 

288 data = _new_data_for_smooth_array() 

289 fwhm = 9 

290 

291 smooth_array(data, affine, fwhm=fwhm, copy=False) 

292 

293 _check_fwhm(data, affine, fwhm) 

294 

295 

296def test_smooth_array_nan_do_not_propagate(): 

297 data = _new_data_for_smooth_array() 

298 data[10, 10, 10] = np.nan 

299 fwhm = 9 

300 affine = AFFINE_TO_TEST[2] 

301 

302 filtered = smooth_array( 

303 data, affine, fwhm=fwhm, ensure_finite=True, copy=True 

304 ) 

305 

306 assert np.all(np.isfinite(filtered)) 

307 

308 

309def test_smooth_array_same_result_with_fwhm_none_or_zero( 

310 smooth_array_data, 

311): 

312 affine = AFFINE_TO_TEST[2] 

313 

314 out_fwhm_none = smooth_array(smooth_array_data, affine, fwhm=None) 

315 out_fwhm_zero = smooth_array(smooth_array_data, affine, fwhm=0.0) 

316 

317 assert_array_equal(out_fwhm_none, out_fwhm_zero) 

318 

319 

320@pytest.mark.parametrize("affine", AFFINE_TO_TEST) 

321def test_fast_smooth_array_give_same_result_as_smooth_array( 

322 smooth_array_data, affine 

323): 

324 assert_equal( 

325 smooth_array(smooth_array_data, affine, fwhm="fast"), 

326 _fast_smooth_array(smooth_array_data), 

327 ) 

328 

329 

330def test_smooth_array_raise_warning_if_fwhm_is_zero(smooth_array_data): 

331 """See https://github.com/nilearn/nilearn/issues/1537.""" 

332 affine = AFFINE_TO_TEST[2] 

333 with pytest.warns(UserWarning): 

334 smooth_array(smooth_array_data, affine, fwhm=0.0) 

335 

336 

337def test_smooth_img(affine_eye, tmp_path): 

338 """Checks added functionalities compared to image._smooth_array().""" 

339 shapes = ((10, 11, 12), (13, 14, 15)) 

340 lengths = (17, 18) 

341 fwhm = (1.0, 2.0, 3.0) 

342 

343 img1, _ = generate_fake_fmri(shape=shapes[0], length=lengths[0]) 

344 img2, _ = generate_fake_fmri(shape=shapes[1], length=lengths[1]) 

345 

346 for create_files in (False, True): 

347 imgs = testing.write_imgs_to_path( 

348 img1, img2, file_path=tmp_path, create_files=create_files 

349 ) 

350 # List of images as input 

351 out = smooth_img(imgs, fwhm) 

352 

353 assert isinstance(out, list) 

354 assert len(out) == 2 

355 for o, s, l in zip(out, shapes, lengths): 

356 assert o.shape == (*s, l) 

357 

358 # Single image as input 

359 out = smooth_img(imgs[0], fwhm) 

360 

361 assert isinstance(out, Nifti1Image) 

362 assert out.shape == (shapes[0] + (lengths[0],)) 

363 

364 # Check corner case situations when fwhm=0, See issue #1537 

365 # Test whether function smooth_img raises a warning when fwhm=0. 

366 with pytest.warns(UserWarning): 

367 smooth_img(img1, fwhm=0.0) 

368 

369 # Test output equal when fwhm=None and fwhm=0 

370 out_fwhm_none = smooth_img(img1, fwhm=None) 

371 out_fwhm_zero = smooth_img(img1, fwhm=0.0) 

372 

373 assert_array_equal(get_data(out_fwhm_none), get_data(out_fwhm_zero)) 

374 

375 data1 = np.zeros((10, 11, 12)) 

376 data1[2:4, 1:5, 3:6] = 1 

377 data2 = np.zeros((13, 14, 15)) 

378 data2[2:4, 1:5, 3:6] = 9 

379 img1_nifti2 = Nifti2Image(data1, affine=affine_eye) 

380 img2_nifti2 = Nifti2Image(data2, affine=affine_eye) 

381 out = smooth_img([img1_nifti2, img2_nifti2], fwhm=1.0) 

382 

383 

384def test_crop_img_to(): 

385 data = np.zeros((5, 6, 7)) 

386 data[2:4, 1:5, 3:6] = 1 

387 affine = np.diag((4, 3, 2, 1)) 

388 img = Nifti1Image(data, affine=affine) 

389 

390 slices = [slice(2, 4), slice(1, 5), slice(3, 6)] 

391 cropped_img = _crop_img_to(img, slices, copy=False) 

392 

393 new_origin = np.array((4, 3, 2)) * np.array((2, 1, 3)) 

394 

395 # check that correct part was extracted: 

396 assert (get_data(cropped_img) == 1).all() 

397 assert cropped_img.shape == (2, 4, 3) 

398 

399 # check that affine was adjusted correctly 

400 assert (cropped_img.affine[:3, 3] == new_origin).all() 

401 

402 # check that data was really not copied 

403 data[2:4, 1:5, 3:6] = 2 

404 

405 assert (get_data(cropped_img) == 2).all() 

406 

407 # check that copying works 

408 copied_cropped_img = _crop_img_to(img, slices) 

409 

410 data[2:4, 1:5, 3:6] = 1 

411 assert (get_data(copied_cropped_img) == 2).all() 

412 

413 

414def test_crop_img(): 

415 data = np.zeros((5, 6, 7)) 

416 data[2:4, 1:5, 3:6] = 1 

417 affine = np.diag((4, 3, 2, 1)) 

418 img = Nifti1Image(data, affine=affine) 

419 

420 cropped_img = crop_img(img, copy_header=True) 

421 

422 # correction for padding with "-1" 

423 # check that correct part was extracted: 

424 # This also corrects for padding 

425 assert (get_data(cropped_img)[1:-1, 1:-1, 1:-1] == 1).all() 

426 assert cropped_img.shape == (2 + 2, 4 + 2, 3 + 2) 

427 

428 

429def test_crop_img_copied_header(img_4d_mni_tr2): 

430 # Test equality of header fields between input and output 

431 # create zero padded data 

432 data = np.zeros((10, 10, 10, 10)) 

433 data[0:4, 0:4, 0:4, :] = 1 

434 # replace the img_4d_mni_tr2 values with data 

435 img_4d_mni_tr2_zero_padded = new_img_like( 

436 img_4d_mni_tr2, 

437 data=data, 

438 affine=img_4d_mni_tr2.affine, 

439 copy_header=True, 

440 ) 

441 cropped_img = crop_img(img_4d_mni_tr2_zero_padded, copy_header=True) 

442 # only dim[1:4] should be different 

443 assert ( 

444 cropped_img.header["dim"][1:4] 

445 != img_4d_mni_tr2_zero_padded.header["dim"][1:4] 

446 ).all() 

447 # other dim indices should be the same 

448 assert ( 

449 cropped_img.header["dim"][0] 

450 == img_4d_mni_tr2_zero_padded.header["dim"][0] 

451 ) 

452 assert ( 

453 cropped_img.header["dim"][4:] 

454 == img_4d_mni_tr2_zero_padded.header["dim"][4:] 

455 ).all() 

456 # other header fields should also be same 

457 match_headers_keys( 

458 cropped_img, img_4d_mni_tr2_zero_padded, except_keys=["dim"] 

459 ) 

460 

461 

462def test_crop_threshold_tolerance(affine_eye): 

463 """Check if crop can skip values that are extremely close to zero. 

464 

465 In a relative sense and will crop them away 

466 """ 

467 data = np.zeros([10, 14, 12]) 

468 data[3:7, 3:7, 5:9] = 1.0 

469 active_shape = (4 + 2, 4 + 2, 4 + 2) # add padding 

470 

471 # add an infinitesimal outside this block 

472 data[3, 3, 3] = 1e-12 

473 img = Nifti1Image(data, affine=affine_eye) 

474 

475 cropped_img = crop_img(img, copy_header=True) 

476 

477 assert cropped_img.shape == active_shape 

478 

479 

480@pytest.mark.parametrize("images_to_mean", _images_to_mean()) 

481def test_mean_img(images_to_mean, tmp_path): 

482 affine = np.diag((4, 3, 2, 1)) 

483 

484 truth = _mean_ground_truth(images_to_mean) 

485 

486 img = mean_img(images_to_mean, copy_header=True) 

487 

488 assert_array_equal(img.affine, affine) 

489 assert_array_equal(get_data(img), truth) 

490 

491 # Test with files 

492 imgs = testing.write_imgs_to_path(*images_to_mean, file_path=tmp_path) 

493 img = mean_img(imgs, copy_header=True) 

494 

495 assert_array_equal(img.affine, affine) 

496 if X64: 

497 assert_array_equal(get_data(img), truth) 

498 else: 

499 # We don't really understand but arrays are not 

500 # exactly equal on 32bit. Given that you can not do 

501 # much real world data analysis with nilearn on a 

502 # 32bit machine it is not worth investigating more 

503 assert_allclose( 

504 get_data(img), 

505 truth, 

506 rtol=np.finfo(truth.dtype).resolution, 

507 atol=0, 

508 ) 

509 

510 

511def test_mean_img_resample(rng): 

512 # Test resampling in mean_img with a permutation of the axes 

513 data = rng.uniform(size=(5, 6, 7, 40)) 

514 affine = np.diag((4, 3, 2, 1)) 

515 img = Nifti1Image(data, affine=affine) 

516 mean_img_to_resample = Nifti1Image(data.mean(axis=-1), affine=affine) 

517 

518 target_affine = affine[:, [1, 0, 2, 3]] # permutation of axes 

519 

520 mean_img_with_resampling = mean_img( 

521 img, target_affine=target_affine, copy_header=True 

522 ) 

523 

524 resampled_mean_image = resample_img( 

525 mean_img_to_resample, 

526 target_affine=target_affine, 

527 copy_header=True, 

528 force_resample=True, 

529 ) 

530 

531 assert_array_equal( 

532 get_data(resampled_mean_image), get_data(mean_img_with_resampling) 

533 ) 

534 assert_array_equal( 

535 resampled_mean_image.affine, mean_img_with_resampling.affine 

536 ) 

537 assert_array_equal(mean_img_with_resampling.affine, target_affine) 

538 

539 

540def test_mean_img_copied_header(img_4d_mni_tr2): 

541 # Test equality of header fields between input and output 

542 result = mean_img(img_4d_mni_tr2, copy_header=True) 

543 match_headers_keys( 

544 result, 

545 img_4d_mni_tr2, 

546 except_keys=["dim", "pixdim", "cal_max", "cal_min"], 

547 ) 

548 

549 

550def test_mean_img_surface(surf_img_1d, surf_img_2d): 

551 """Check that mean is properly computed over 'time points'.""" 

552 # one 'time point' image returns same 

553 img = mean_img(surf_img_1d) 

554 

555 assert_surface_image_equal(img, surf_img_1d) 

556 

557 # image with left hemisphere 

558 # where timepoint 1 has all values == 0 

559 # and timepoint 2 == 1 

560 two_time_points_img = surf_img_2d(2) 

561 two_time_points_img.data.parts["left"][:, 0] = np.zeros(shape=4) 

562 two_time_points_img.data.parts["left"][:, 1] = np.ones(shape=4) 

563 

564 img = mean_img(two_time_points_img) 

565 

566 assert_array_equal(img.data.parts["left"], np.ones(shape=(4,)) * 0.5) 

567 assert img.shape == (img.mesh.n_vertices,) 

568 

569 

570def test_mean_img_surface_list(surf_img_2d): 

571 """Check that mean_img computes mean of mean.""" 

572 surf_img_1 = surf_img_2d(2) 

573 surf_img_2 = surf_img_2d(3) 

574 

575 mean_surf_img_1 = mean_img(surf_img_1) 

576 mean_surf_img_2 = mean_img(surf_img_2) 

577 

578 direct_mean = mean_img([surf_img_1, surf_img_2]) 

579 

580 assert_surface_image_equal( 

581 direct_mean, mean_img([mean_surf_img_1, mean_surf_img_2]) 

582 ) 

583 

584 

585def test_swap_img_hemispheres(affine_eye, shape_3d_default, rng): 

586 # make sure input image data is not overwritten inside function 

587 data = rng.standard_normal(size=shape_3d_default) 

588 data_img = Nifti1Image(data, affine_eye) 

589 

590 swap_img_hemispheres(data_img) 

591 

592 assert_array_equal(get_data(data_img), data) 

593 # swapping operations work 

594 assert_array_equal( # one turn 

595 get_data(swap_img_hemispheres(data_img)), data[::-1] 

596 ) 

597 assert_array_equal( # two turns -> back to original data 

598 get_data(swap_img_hemispheres(swap_img_hemispheres(data_img))), 

599 data, 

600 ) 

601 

602 

603def test_index_img_error_3d(affine_eye): 

604 img_3d = Nifti1Image(np.ones((3, 4, 5)), affine_eye) 

605 expected_error_msg = ( 

606 "Input data has incompatible dimensionality: " 

607 "Expected dimension is 4D and you provided " 

608 "a 3D image." 

609 ) 

610 with pytest.raises(TypeError, match=expected_error_msg): 

611 index_img(img_3d, 0) 

612 

613 

614def test_index_img(): 

615 img_4d, _ = generate_fake_fmri(affine=NON_EYE_AFFINE) 

616 

617 fourth_dim_size = img_4d.shape[3] 

618 tested_indices = [ 

619 *range(fourth_dim_size), 

620 slice(2, 8, 2), 

621 [1, 2, 3, 2], 

622 (np.arange(fourth_dim_size) % 3) == 1, 

623 ] 

624 for i in tested_indices: 

625 this_img = index_img(img_4d, i) 

626 

627 expected_data_3d = get_data(img_4d)[..., i] 

628 assert_array_equal(get_data(this_img), expected_data_3d) 

629 assert_array_equal(this_img.affine, img_4d.affine) 

630 

631 

632def test_index_img_error_4d(affine_eye): 

633 img_4d, _ = generate_fake_fmri(affine=affine_eye) 

634 fourth_dim_size = img_4d.shape[3] 

635 for i in [ 

636 fourth_dim_size, 

637 -fourth_dim_size - 1, 

638 [0, fourth_dim_size], 

639 np.repeat(True, fourth_dim_size + 1), 

640 ]: 

641 with pytest.raises( 

642 IndexError, 

643 match="out of bounds|invalid index|out of range|boolean index", 

644 ): 

645 index_img(img_4d, i) 

646 

647 

648def test_pd_index_img(rng, img_4d_rand_eye): 

649 # confirm indices from pandas dataframes are handled correctly 

650 fourth_dim_size = img_4d_rand_eye.shape[3] 

651 

652 arr = rng.uniform(size=fourth_dim_size) > 0.5 

653 

654 np_index_img = index_img(img_4d_rand_eye, arr) 

655 pd_index_img = index_img(img_4d_rand_eye, pd.DataFrame({"arr": arr})) 

656 

657 assert_array_equal(get_data(np_index_img), get_data(pd_index_img)) 

658 

659 

660def test_iter_img_3d_imag_error(affine_eye): 

661 img_3d = Nifti1Image(np.ones((3, 4, 5)), affine_eye) 

662 expected_error_msg = ( 

663 "Input data has incompatible dimensionality: " 

664 "Expected dimension is 4D and you provided " 

665 "a 3D image." 

666 ) 

667 with pytest.raises(TypeError, match=expected_error_msg): 

668 iter_img(img_3d) 

669 

670 

671@pytest.mark.timeout(0) 

672def test_iter_img(tmp_path): 

673 img_4d, _ = generate_fake_fmri(affine=NON_EYE_AFFINE) 

674 

675 for i, img in enumerate(iter_img(img_4d)): 

676 expected_data_3d = get_data(img_4d)[..., i] 

677 

678 assert_array_equal(get_data(img), expected_data_3d) 

679 assert_array_equal(img.affine, img_4d.affine) 

680 

681 img_4d_filename = testing.write_imgs_to_path(img_4d, file_path=tmp_path) 

682 for i, img in enumerate(iter_img(img_4d_filename)): 

683 expected_data_3d = get_data(img_4d)[..., i] 

684 

685 assert_array_equal(get_data(img), expected_data_3d) 

686 assert_array_equal(img.affine, img_4d.affine) 

687 

688 # enables to delete "img_4d_filename" on windows 

689 del img 

690 

691 img_3d_list = list(iter_img(img_4d)) 

692 for i, img in enumerate(iter_img(img_3d_list)): 

693 expected_data_3d = get_data(img_4d)[..., i] 

694 

695 assert_array_equal(get_data(img), expected_data_3d) 

696 assert_array_equal(img.affine, img_4d.affine) 

697 

698 img_3d_filenames = testing.write_imgs_to_path( 

699 *img_3d_list, file_path=tmp_path 

700 ) 

701 for i, img in enumerate(iter_img(img_3d_filenames)): 

702 expected_data_3d = get_data(img_4d)[..., i] 

703 

704 assert_array_equal(get_data(img), expected_data_3d) 

705 assert_array_equal(img.affine, img_4d.affine) 

706 

707 # enables to delete "img_3d_filename" on windows 

708 del img 

709 

710 

711def test_iter_surface_img(surf_img_2d): 

712 """Check iter_img returns list of SurfaceImage. 

713 

714 Each SurfaceImage must have same mesh as input 

715 and data from one of the sample of the input SurfaceImage. 

716 """ 

717 input = surf_img_2d(5) 

718 output = list(iter_img(input)) 

719 

720 assert isinstance(output, list) 

721 assert len(output) == input.shape[1] 

722 assert all(isinstance(x, SurfaceImage) for x in output) 

723 for i in range(input.shape[1]): 

724 assert_polymesh_equal(output[i].mesh, input.mesh) 

725 assert_array_equal( 

726 np.squeeze(output[i].data.parts["left"]), 

727 input.data.parts["left"][..., i], 

728 ) 

729 

730 

731def test_iter_img_surface_2d(surf_img_1d, surf_img_2d): 

732 """Return as is if surface image is 2D.""" 

733 input = surf_img_2d(1) 

734 output = list(iter_img(input)) 

735 

736 assert_surface_image_equal(output[0], input) 

737 

738 output = list(iter_img(surf_img_1d)) 

739 

740 assert_surface_image_equal(output[0], surf_img_1d) 

741 

742 

743def test_new_img_like_mgz(): 

744 """Check that new images can be generated with bool MGZ type. 

745 

746 This is usually when computing masks using MGZ inputs, e.g. 

747 when using plot_stap_map 

748 """ 

749 img_filename = Path(__file__).parent / "data" / "test.mgz" 

750 ref_img = load(img_filename) 

751 data = np.ones(get_data(ref_img).shape, dtype=bool) 

752 affine = ref_img.affine 

753 new_img_like(ref_img, data, affine, copy_header=False) 

754 

755 

756def test_new_img_like(): 

757 # Give a list to new_img_like 

758 data = np.zeros((5, 6, 7)) 

759 data[2:4, 1:5, 3:6] = 1 

760 affine = np.diag((4, 3, 2, 1)) 

761 img = Nifti1Image(data, affine=affine) 

762 

763 img2 = new_img_like([img], data) 

764 

765 assert_array_equal(get_data(img), get_data(img2)) 

766 

767 # test_new_img_like_with_nifti2image_copy_header 

768 img_nifti2 = Nifti2Image(data, affine=affine) 

769 

770 img2_nifti2 = new_img_like([img_nifti2], data, copy_header=True) 

771 

772 assert_array_equal(get_data(img_nifti2), get_data(img2_nifti2)) 

773 

774 

775def test_new_img_like_accepts_paths(affine_eye, tmp_path, rng): 

776 """Check that new_img_like can accept instances of pathlib.Path.""" 

777 nifti_path = tmp_path / "sample.nii" 

778 assert isinstance(nifti_path, Path) 

779 

780 data = rng.random((10, 10, 10)) 

781 img = Nifti1Image(data, affine_eye) 

782 img.to_filename(nifti_path) 

783 

784 new_data = rng.random((10, 10, 10)) 

785 new_img = new_img_like(nifti_path, new_data) 

786 assert new_img.shape == (10, 10, 10) 

787 

788 # Check that list of pathlib.Path also accepted 

789 new_img = new_img_like([nifti_path], new_data) 

790 assert new_img.shape == (10, 10, 10) 

791 

792 

793def test_new_img_like_non_iterable_header(rng): 

794 """Tests that when an niimg's header is not iterable \ 

795 and it is set to be copied, an error is not raised. 

796 """ 

797 fake_fmri_data = rng.uniform(size=_shape_4d_default()) 

798 fake_affine = rng.uniform(size=(4, 4)) 

799 fake_spatial_image = spatialimages.SpatialImage( 

800 fake_fmri_data, fake_affine 

801 ) 

802 

803 assert new_img_like( 

804 fake_spatial_image, data=fake_fmri_data, copy_header=True 

805 ) 

806 

807 

808@pytest.mark.parametrize("no_int64_nifti", ["allow for this test"]) 

809def test_new_img_like_int64(shape_3d_default): 

810 img = generate_labeled_regions(shape=shape_3d_default, n_regions=2) 

811 

812 data = get_data(img).astype("int32") 

813 

814 with warnings.catch_warnings(): 

815 warnings.simplefilter("error") 

816 new_img = new_img_like(img, data) 

817 assert get_data(new_img).dtype == "int32" 

818 

819 data = data.astype("int64") 

820 

821 with pytest.warns(UserWarning, match=r".*array.*contains.*64.*"): 

822 new_img = new_img_like(img, data) 

823 assert get_data(new_img).dtype == "int32" 

824 

825 data[:] = 2**40 

826 

827 with pytest.warns(UserWarning, match=r".*64.*too large.*"): 

828 new_img = new_img_like(img, data, copy_header=True) 

829 assert get_data(new_img).dtype == "int64" 

830 

831 

832def test_input_in_threshold_img( 

833 shape_3d_default, surf_img_1d, surf_mask_1d, affine_eye 

834): 

835 """Check threshold_img only works with surface OR volume.""" 

836 threshold = 0.5 

837 

838 # setting copy_header to True to avoid warnings 

839 # TODO remove when bumping to nilearn > 0.13 

840 copy_header = True 

841 

842 vol_img, _ = generate_maps(shape_3d_default, n_regions=2) 

843 vol_mask = Nifti1Image(np.ones(shape_3d_default), affine_eye) 

844 

845 # All of those should be OK 

846 thr_img = threshold_img( 

847 vol_img, threshold=threshold, mask_img=None, copy_header=copy_header 

848 ) 

849 

850 _check_thresholded_output(vol_img, thr_img, threshold) 

851 

852 thr_img = threshold_img( 

853 surf_img_1d, 

854 threshold=threshold, 

855 mask_img=None, 

856 copy_header=copy_header, 

857 ) 

858 

859 _check_thresholded_output(surf_img_1d, thr_img, threshold) 

860 

861 # same but with a mask 

862 threshold_img( 

863 vol_img, 

864 threshold=threshold, 

865 mask_img=vol_mask, 

866 copy_header=copy_header, 

867 ) 

868 threshold_img( 

869 surf_img_1d, 

870 threshold=threshold, 

871 mask_img=surf_mask_1d, 

872 copy_header=copy_header, 

873 ) 

874 

875 

876def test_input_in_threshold_img_several_timepoints( 

877 img_4d_rand_eye, surf_img_2d 

878): 

879 """Check threshold_img works with 2D surface OR 4D volume.""" 

880 threshold = 0.5 

881 

882 # setting copy_header to True to avoid warnings 

883 # TODO remove when bumping to nilearn > 0.13 

884 copy_header = True 

885 thr_img = threshold_img( 

886 img_4d_rand_eye, threshold=0.5, copy_header=copy_header 

887 ) 

888 

889 _check_thresholded_output(img_4d_rand_eye, thr_img, threshold) 

890 

891 original_image = surf_img_2d(5) 

892 thr_img = threshold_img(original_image, threshold=0.5) 

893 

894 _check_thresholded_output(original_image, thr_img, threshold) 

895 

896 

897def _check_thresholded_output(input, output, threshold): 

898 """Check data was properly thresholed. 

899 

900 Assumes: 

901 - a one-sided threshold that keeps data > threshold 

902 - no extra mask used 

903 """ 

904 if isinstance(input, Nifti1Image): 

905 original_data = input.get_fdata() 

906 data = output.get_fdata() 

907 elif isinstance(input, SurfaceImage): 

908 original_data = get_surface_data(input) 

909 data = get_surface_data(output) 

910 

911 non_zero = data != 0 

912 assert np.all(data[non_zero] > threshold) 

913 

914 data_to_mask = original_data < threshold 

915 assert np.all(data[data_to_mask] == 0) 

916 

917 

918def test_input_in_threshold_img_errors( 

919 shape_3d_default, surf_img_1d, surf_mask_1d, affine_eye 

920): 

921 """Check invalid inputs to threshold_img .""" 

922 vol_img, _ = generate_maps(shape_3d_default, n_regions=2) 

923 vol_mask = Nifti1Image(np.ones(shape_3d_default), affine_eye) 

924 

925 # invalid input: img is an int 

926 with pytest.raises( 

927 TypeError, 

928 match="'img' should be a 3D/4D Niimg-like object or a SurfaceImage.", 

929 ): 

930 threshold_img(img=1, threshold=1) 

931 

932 # incompatible inputs raise errors 

933 with pytest.raises( 

934 TypeError, 

935 match="Mask and images to fit must be of compatible types.", 

936 ): 

937 threshold_img(vol_img, threshold=1, mask_img=surf_mask_1d) 

938 with pytest.raises( 

939 TypeError, 

940 match="Mask and images to fit must be of compatible types.", 

941 ): 

942 threshold_img(surf_img_1d, threshold=1, mask_img=vol_mask) 

943 

944 

945def test_threshold_img_warning(surf_img_1d): 

946 """Check warnings thrown by threshold_img.""" 

947 with pytest.warns( 

948 UserWarning, 

949 match="Cluster thresholding not implemented for SurfaceImage.", 

950 ): 

951 threshold_img(surf_img_1d, threshold=1, cluster_threshold=10) 

952 

953 

954@pytest.mark.parametrize("two_sided", [True, False]) 

955def test_validity_threshold_value_in_threshold_img( 

956 shape_3d_default, two_sided 

957): 

958 """Check that invalid values to threshold_img's threshold parameter \ 

959 raise Exceptions. 

960 """ 

961 # setting copy_header to True to avoid warnings 

962 # TODO remove when bumping to nilearn > 0.13 

963 copy_header = True 

964 maps, _ = generate_maps(shape_3d_default, n_regions=2) 

965 

966 # testing to raise same error when threshold=None case 

967 with pytest.raises( 

968 TypeError, 

969 match="threshold should be either a number or a string", 

970 ): 

971 threshold_img(maps, threshold=None, copy_header=copy_header) 

972 

973 threshold = object() 

974 with pytest.raises( 

975 TypeError, match="should be either a number or a string" 

976 ): 

977 threshold_img( 

978 maps, 

979 threshold=threshold, 

980 two_sided=two_sided, 

981 copy_header=copy_header, 

982 ) 

983 

984 invalid_threshold_values = ["90t%", "s%", "t", "0.1"] 

985 name = "threshold" 

986 for thr in invalid_threshold_values: 

987 with pytest.raises( 

988 ValueError, 

989 match=f"{name}.+should be a number followed by the percent", 

990 ): 

991 threshold_img( 

992 maps, 

993 threshold=thr, 

994 copy_header=copy_header, 

995 two_sided=two_sided, 

996 ) 

997 

998 

999def test_validity_negative_threshold_value_in_threshold_img(shape_3d_default): 

1000 """Check that negative values to threshold_img's threshold parameter \ 

1001 raise Exceptions. 

1002 """ 

1003 # setting copy_header to True to avoid warnings 

1004 # TODO remove when bumping to nilearn > 0.13 

1005 copy_header = True 

1006 

1007 maps, _ = generate_maps(shape_3d_default, n_regions=2) 

1008 

1009 # invalid threshold values when two_sided=True 

1010 thresholds = [-10, "-10%"] 

1011 for wrong_threshold in thresholds: 

1012 with pytest.raises(ValueError, match="should not be a negative"): 

1013 threshold_img( 

1014 maps, 

1015 threshold=wrong_threshold, 

1016 two_sided=True, 

1017 copy_header=copy_header, 

1018 ) 

1019 

1020 with pytest.raises(ValueError, match="should not be a negative"): 

1021 threshold_img( 

1022 maps, threshold="-10%", two_sided=False, copy_header=copy_header 

1023 ) 

1024 

1025 

1026def test_threshold_img(affine_eye): 

1027 """Smoke test for threshold_img with valid threshold inputs.""" 

1028 # setting copy_header to True to avoid warnings 

1029 # TODO remove when bumping to nilearn > 0.13 

1030 copy_header = True 

1031 

1032 shape = (10, 20, 30) 

1033 maps, _ = generate_maps(shape, n_regions=4) 

1034 mask_img = Nifti1Image(np.ones((shape), dtype=np.int8), affine_eye) 

1035 

1036 for img in iter_img(maps): 

1037 # when threshold is a float value 

1038 threshold_img(img, threshold=0.8, copy_header=copy_header) 

1039 

1040 # when we provide mask image 

1041 threshold_img( 

1042 img, threshold=1, mask_img=mask_img, copy_header=copy_header 

1043 ) 

1044 

1045 # when threshold is a percentile 

1046 threshold_img(img, threshold="2%", copy_header=copy_header) 

1047 

1048 

1049@pytest.mark.parametrize( 

1050 "threshold, expected_n_non_zero", 

1051 [ 

1052 (1, {"left": 2, "right": 4}), 

1053 (10, {"left": 0, "right": 3}), 

1054 (50, {"left": 0, "right": 0}), 

1055 ("50%", {"left": 0, "right": 4}), 

1056 ], 

1057) 

1058def test_threshold_surf_img_1d(surf_img_1d, threshold, expected_n_non_zero): 

1059 """Check number of elements surviving thresholding 1D surface image. 

1060 

1061 For left hemisphere: 1 < values < 10 

1062 For right hemisphere: 10 < values < 50 

1063 """ 

1064 thr_img = threshold_img(surf_img_1d, threshold=threshold) 

1065 for hemi in thr_img.data.parts: 

1066 data = thr_img.data.parts[hemi] 

1067 assert len(data[np.nonzero(data)]) == expected_n_non_zero[hemi] 

1068 

1069 

1070@pytest.mark.parametrize( 

1071 "threshold, expected_n_non_zero", 

1072 [ 

1073 (0.9, {"left": 2, "right": 3}), 

1074 (9, {"left": 0, "right": 3}), 

1075 (50, {"left": 0, "right": 0}), 

1076 ("50%", {"left": 0, "right": 2}), 

1077 ], 

1078) 

1079def test_threshold_surf_img_1d_with_mask( 

1080 surf_img_1d, threshold, expected_n_non_zero, surf_mask_1d 

1081): 

1082 """Check number of elements surviving thresholding 1D surface image. 

1083 

1084 Fewer elements survive thresholding when a mask is provided. 

1085 

1086 For left hemisphere: 1 <= values < 10 

1087 For right hemisphere: 10 <= values < 50 

1088 """ 

1089 thr_img = threshold_img( 

1090 surf_img_1d, threshold=threshold, mask_img=surf_mask_1d 

1091 ) 

1092 for hemi in thr_img.data.parts: 

1093 data = thr_img.data.parts[hemi] 

1094 assert len(data[np.nonzero(data)]) == expected_n_non_zero[hemi] 

1095 

1096 

1097@pytest.mark.parametrize( 

1098 "threshold, expected_n_non_zero, two_sided", 

1099 [ 

1100 (1, {"left": 0, "right": 4}, False), 

1101 (29, {"left": 0, "right": 2}, False), 

1102 (50, {"left": 0, "right": 0}, False), 

1103 ("50%", {"left": 0, "right": 3}, False), 

1104 (1, {"left": 3, "right": 4}, True), 

1105 (29, {"left": 1, "right": 2}, True), 

1106 (50, {"left": 0, "right": 0}, True), 

1107 ("50%", {"left": 1, "right": 2}, True), 

1108 ], 

1109) 

1110def test_threshold_surf_img_1d_negative_values( 

1111 surf_img_1d, threshold, expected_n_non_zero, two_sided 

1112): 

1113 """Check number of elements surviving thresholding 1D surface image. 

1114 

1115 Data also includes negative values. 

1116 

1117 Also test 2 sided thresholding. 

1118 

1119 For left hemisphere: -41 < values < -9 

1120 For right hemisphere: 9 < values < 50 

1121 """ 

1122 surf_img_1d.data.parts["left"] *= -10 

1123 

1124 thr_img = threshold_img( 

1125 surf_img_1d, threshold=threshold, two_sided=two_sided 

1126 ) 

1127 for hemi in thr_img.data.parts: 

1128 data = thr_img.data.parts[hemi] 

1129 assert len(data[np.nonzero(data)]) == expected_n_non_zero[hemi] 

1130 

1131 

1132@pytest.mark.parametrize( 

1133 "threshold, two_sided, expected", 

1134 [ 

1135 (3, True, 16), 

1136 (3, False, 8), 

1137 (4, True, 0), 

1138 (4, False, 0), 

1139 (0, True, 448), 

1140 (0, False, 224), 

1141 (-3, False, 8), 

1142 (-0.5, False, 224), 

1143 ("0%", True, 448), 

1144 ("10%", False, 224), 

1145 ("99%", False, 8), 

1146 ("99%", True, 16), 

1147 ("100%", True, 0), 

1148 ], 

1149) 

1150def test_threshold_img_with_mask( 

1151 stat_img_test_data, affine_eye, threshold, two_sided, expected 

1152): 

1153 """Tests `nilearn.image.threshold_img` for float and str values using 

1154 mask. 

1155 """ 

1156 temp_mask = np.ones((stat_img_test_data.shape), dtype=np.int8) 

1157 

1158 temp_mask[8:11, 0, 0] = 0 # mask values 5 

1159 temp_mask[13:16, 0, 0] = 0 # mask values -5 

1160 temp_mask[19:, 10:, 6:] = 0 

1161 temp_mask[:4, 10:, 6:] = 0 

1162 temp_mask[13:19, 10:, 6:] = 0 

1163 temp_mask[13:19, 0:4, 6:] = 0 

1164 mask_img = Nifti1Image(temp_mask, affine_eye) 

1165 

1166 thr_img = threshold_img( 

1167 img=stat_img_test_data, 

1168 mask_img=mask_img, 

1169 threshold=threshold, 

1170 two_sided=two_sided, 

1171 copy_header=True, 

1172 ) 

1173 

1174 img_data = thr_img.get_fdata() 

1175 assert len(img_data[np.nonzero(img_data)]) == expected 

1176 

1177 

1178@pytest.mark.parametrize( 

1179 "threshold,two_sided,cluster_threshold,expected", 

1180 [ 

1181 (2, False, 0, [0, 4, 5]), # cluster with values > 2 

1182 (2, True, 0, [-5, -4, 0, 4, 5]), # cluster with |values| > 2 

1183 (2, True, 5, [-4, 0, 4]), # cluster: |values| > 2 & size > 5 

1184 (2, True, 5, [-4, 0, 4]), # cluster: |values| > 2 & size > 5 

1185 (0.5, True, 5, [-4, -1, 0, 1, 4]), # cluster: |values| > 0.5 & size>5 

1186 (0.5, False, 5, [0, 1, 4]), # cluster: values > 0.5 & size > 5 

1187 ], 

1188) 

1189def test_threshold_img_with_cluster_threshold( 

1190 stat_img_test_data, threshold, two_sided, cluster_threshold, expected 

1191): 

1192 """Check that passing specific threshold and cluster threshold values \ 

1193 only gives cluster the right number of voxels with the right values. 

1194 """ 

1195 thr_img = threshold_img( 

1196 img=stat_img_test_data, 

1197 threshold=threshold, 

1198 two_sided=two_sided, 

1199 cluster_threshold=cluster_threshold, 

1200 copy_header=True, 

1201 ) 

1202 

1203 assert np.array_equal(np.unique(thr_img.get_fdata()), np.array(expected)) 

1204 

1205 

1206def test_threshold_img_threshold_n_clusters(stat_img_test_data): 

1207 """With a cluster threshold of 5 we get 8 clusters with |values| > 2 \ 

1208 and cluster sizes > 5. 

1209 """ 

1210 thr_img = threshold_img( 

1211 img=stat_img_test_data, 

1212 threshold=2, 

1213 two_sided=True, 

1214 cluster_threshold=5, 

1215 copy_header=True, 

1216 ) 

1217 

1218 assert np.sum(thr_img.get_fdata() == 4) == 8 

1219 

1220 

1221def test_threshold_img_no_copy_surface(surf_img_1d): 

1222 """Test copy=False on surface data. 

1223 

1224 Check that not copying does mutate the original image. 

1225 """ 

1226 threshold = 15 

1227 input_img = surf_img_1d 

1228 result = threshold_img(input_img, threshold=threshold, copy=False) 

1229 assert_surface_image_equal(result, surf_img_1d) 

1230 

1231 

1232def test_threshold_img_copy_surface(surf_img_1d): 

1233 """Test copy=True on surface data. 

1234 

1235 Check that copying does not mutate the original image. 

1236 """ 

1237 threshold = 15 

1238 input_img = surf_img_1d 

1239 result = threshold_img(input_img, threshold=threshold, copy=True) 

1240 with pytest.raises(ValueError): 

1241 assert_surface_image_equal(result, surf_img_1d) 

1242 

1243 

1244def test_threshold_img_copy_volume(img_4d_ones_eye): 

1245 """Test the behavior of threshold_img's copy parameter.""" 

1246 threshold = 1 

1247 # Check that copy does not mutate. It returns modified copy. 

1248 thr_img = threshold_img(img_4d_ones_eye, threshold, copy_header=True) 

1249 

1250 # Original img_ones should have all ones. 

1251 assert_array_equal(get_data(img_4d_ones_eye), np.ones(_shape_4d_default())) 

1252 # Thresholded should have all zeros. 

1253 assert_array_equal(get_data(thr_img), np.zeros(_shape_4d_default())) 

1254 

1255 # Check that not copying does mutate. 

1256 img_to_mutate = img_4d_ones_eye 

1257 

1258 thr_img = threshold_img( 

1259 img_to_mutate, threshold, copy=False, copy_header=True 

1260 ) 

1261 

1262 # Check that original mutates 

1263 assert_array_equal(get_data(img_to_mutate), np.zeros(_shape_4d_default())) 

1264 # And that returned value is also thresholded. 

1265 assert_array_equal(get_data(img_to_mutate), get_data(thr_img)) 

1266 

1267 

1268def test_isnan_threshold_img_data(affine_eye, shape_3d_default): 

1269 """Check threshold_img converges properly when input image has nans.""" 

1270 maps, _ = generate_maps(shape_3d_default, n_regions=2) 

1271 data = get_data(maps) 

1272 data[:, :, 0] = np.nan 

1273 

1274 maps_img = Nifti1Image(data, affine_eye) 

1275 

1276 threshold_img(maps_img, threshold=0.8, copy_header=True) 

1277 

1278 

1279def test_threshold_img_copied_header(img_4d_mni_tr2): 

1280 # Test equality of header fields between input and output 

1281 thr_img = threshold_img(img_4d_mni_tr2, threshold=0.5, copy_header=True) 

1282 # only the min value should be different 

1283 match_headers_keys( 

1284 thr_img, 

1285 img_4d_mni_tr2, 

1286 except_keys=["cal_min"], 

1287 ) 

1288 # min value should be 0 in the result 

1289 assert thr_img.header["cal_min"] == 0 

1290 

1291 

1292def test_math_img_exceptions(affine_eye, img_4d_ones_eye, surf_img_2d): 

1293 img1 = img_4d_ones_eye 

1294 img2 = Nifti1Image(np.zeros((10, 20, 10, 10)), affine_eye) 

1295 img3 = img_4d_ones_eye 

1296 img4 = Nifti1Image(np.ones(_shape_4d_default()), affine_eye * 2) 

1297 

1298 formula = "np.mean(img1, axis=-1) - np.mean(img2, axis=-1)" 

1299 # Images with different shapes should raise a ValueError exception. 

1300 with pytest.raises(ValueError, match="Input images cannot be compared"): 

1301 math_img(formula, img1=img1, img2=img2) 

1302 

1303 # Images with different affines should raise a ValueError exception. 

1304 with pytest.raises(ValueError, match="Input images cannot be compared"): 

1305 math_img(formula, img1=img1, img2=img4) 

1306 

1307 bad_formula = "np.toto(img1, axis=-1) - np.mean(img3, axis=-1)" 

1308 with pytest.raises( 

1309 AttributeError, match="Input formula couldn't be processed" 

1310 ): 

1311 math_img(bad_formula, img1=img1, img3=img3) 

1312 # Same but for surface data 

1313 with pytest.raises( 

1314 AttributeError, match="Input formula couldn't be processed" 

1315 ): 

1316 math_img(bad_formula, img1=surf_img_2d(2), img3=surf_img_2d(3)) 

1317 

1318 # Test copy_header_from parameter 

1319 # Copying header from 4d image to a result that is 3d should raise a 

1320 # ValueError 

1321 formula = "np.mean(img1, axis=-1) - np.mean(img3, axis=-1)" 

1322 with pytest.raises(ValueError, match="Cannot copy the header."): 

1323 math_img(formula, img1=img1, img3=img3, copy_header_from="img1") 

1324 

1325 # Passing an 'img*' variable (to copy_header_from) that is not in the 

1326 # formula or an img* argument should raise a KeyError exception. 

1327 with pytest.raises(KeyError): 

1328 math_img(formula, img1=img1, img3=img3, copy_header_from="img2") 

1329 

1330 

1331def test_math_img( 

1332 affine_eye, img_4d_ones_eye, img_4d_zeros_eye, shape_3d_default, tmp_path 

1333): 

1334 img1 = img_4d_ones_eye 

1335 img2 = img_4d_zeros_eye 

1336 expected_result = Nifti1Image(np.ones(shape_3d_default), affine_eye) 

1337 

1338 formula = "np.mean(img1, axis=-1) - np.mean(img2, axis=-1)" 

1339 for create_files in (True, False): 

1340 imgs = testing.write_imgs_to_path( 

1341 img1, img2, file_path=tmp_path, create_files=create_files 

1342 ) 

1343 result = math_img(formula, img1=imgs[0], img2=imgs[1]) 

1344 assert_array_equal(get_data(result), get_data(expected_result)) 

1345 assert_array_equal(result.affine, expected_result.affine) 

1346 assert result.shape == expected_result.shape 

1347 

1348 

1349def test_math_img_surface(surf_img_2d): 

1350 """Test math_img on surface data.""" 

1351 img1 = surf_img_2d(1) 

1352 img2 = surf_img_2d(3) 

1353 

1354 tmp = {} 

1355 for part in img1.data.parts: 

1356 tmp[part] = np.mean(img1.data.parts[part], axis=-1) - np.mean( 

1357 img2.data.parts[part], axis=-1 

1358 ) 

1359 

1360 expected_result = SurfaceImage(mesh=img1.mesh, data=tmp) 

1361 

1362 formula = "np.mean(img1, axis=-1) - np.mean(img2, axis=-1)" 

1363 result = math_img(formula, img1=img1, img2=img2) 

1364 

1365 assert isinstance(result, SurfaceImage) 

1366 assert_surface_image_equal(result, expected_result) 

1367 

1368 

1369def test_math_img_copy_default_header( 

1370 img_4d_ones_eye_default_header, img_4d_ones_eye_tr2 

1371): 

1372 # case where data values are not changed and header values are not copied 

1373 # the result should have default header values 

1374 formula_no_change = "img * 1" 

1375 # using img_4d_ones_eye_tr2 with edited header in the formula 

1376 result = math_img( 

1377 formula_no_change, img=img_4d_ones_eye_tr2, copy_header_from=None 

1378 ) 

1379 # header values should instead match default header values 

1380 assert result.header == img_4d_ones_eye_default_header.header 

1381 

1382 

1383def test_math_img_copied_header_from_img(img_4d_mni_tr2): 

1384 # case where data values are not changed but header values are copied 

1385 # the result should have the same header values as the given image 

1386 formula_no_change = "img * 1" 

1387 result = math_img( 

1388 formula_no_change, img=img_4d_mni_tr2, copy_header_from="img" 

1389 ) 

1390 # all header values should be the same 

1391 assert result.header == img_4d_mni_tr2.header 

1392 

1393 

1394def test_math_img_copied_header_data_values_changed( 

1395 img_4d_ones_eye_default_header, img_4d_ones_eye_tr2 

1396): 

1397 # case where data values are changed and header values are copied from one 

1398 # of the input images 

1399 # the result should have the same header values as img_4d_ones_eye_tr2, 

1400 # except for cal_max and cal_min that should be different 

1401 formula_change_min_max = "img1 - img2" 

1402 result = math_img( 

1403 formula_change_min_max, 

1404 img1=img_4d_ones_eye_default_header, 

1405 img2=img_4d_ones_eye_tr2, 

1406 copy_header_from="img2", 

1407 ) 

1408 for key in img_4d_ones_eye_tr2.header: 

1409 # cal_max and cal_min should be different in result 

1410 if key in ["cal_max", "cal_min"]: 

1411 assert result.header[key] != img_4d_ones_eye_tr2.header[key] 

1412 # other header values should be the same 

1413 elif isinstance(result.header[key], np.ndarray): 

1414 assert_array_equal( 

1415 result.header[key], img_4d_ones_eye_tr2.header[key] 

1416 ) 

1417 else: 

1418 assert result.header[key] == img_4d_ones_eye_tr2.header[key] 

1419 

1420 

1421def test_binarize_img(img_4d_rand_eye): 

1422 # Test that all output values are 1. 

1423 img1 = binarize_img(img_4d_rand_eye, copy_header=True) 

1424 

1425 assert_array_equal(np.unique(img1.dataobj), np.array([1])) 

1426 

1427 # Test that it works with threshold 

1428 img2 = binarize_img(img_4d_rand_eye, threshold=0.5, copy_header=True) 

1429 

1430 assert_array_equal(np.unique(img2.dataobj), np.array([0, 1])) 

1431 # Test that manual binarization equals binarize_img results. 

1432 img3 = copy_img(img_4d_rand_eye) 

1433 img3.dataobj[img_4d_rand_eye.dataobj < 0.5] = 0 

1434 img3.dataobj[img_4d_rand_eye.dataobj >= 0.5] = 1 

1435 

1436 assert_array_equal(img2.dataobj, img3.dataobj) 

1437 

1438 

1439def test_binarize_img_surface(surf_img_1d): 

1440 """Test binarize_img on surface data.""" 

1441 img = surf_img_1d 

1442 for k, v in img.data.parts.items(): 

1443 img.data.parts[k] = v + 1 

1444 # Test that all output values are 1. 

1445 img1 = binarize_img(surf_img_1d) 

1446 

1447 assert_array_equal(np.unique(get_surface_data(img1)), np.array([1])) 

1448 

1449 # Test that it works with threshold 

1450 img2 = binarize_img(surf_img_1d, threshold=9) 

1451 assert_array_equal(np.unique(get_surface_data(img2)), np.array([0, 1])) 

1452 

1453 

1454def test_binarize_negative_img(img_4d_rand_eye, rng): 

1455 # Test option to use original or absolute values 

1456 img_data = img_4d_rand_eye.dataobj 

1457 # Create a mask for half of the values and make them negative 

1458 neg_mask = rng.choice( 

1459 [True, False], size=img_4d_rand_eye.shape, p=[0.5, 0.5] 

1460 ) 

1461 img_data[neg_mask] *= -1 

1462 img = new_img_like(img_4d_rand_eye, img_data) 

1463 # Binarize using original and absolute values 

1464 img_original = binarize_img( 

1465 img, threshold=0, two_sided=False, copy_header=True 

1466 ) 

1467 img_absolute = binarize_img( 

1468 img, threshold=0, two_sided=True, copy_header=True 

1469 ) 

1470 # Check that all values are 1 for absolute valued threshold 

1471 assert_array_equal(np.unique(img_absolute.dataobj), np.array([1])) 

1472 # Check that binarized image contains 0 and 1 for original threshold 

1473 assert_array_equal(np.unique(img_original.dataobj), np.array([0, 1])) 

1474 

1475 

1476def test_binarize_img_copied_header(img_4d_mni_tr2): 

1477 # Test equality of header fields between input and output 

1478 result = binarize_img(img_4d_mni_tr2, threshold=0.5, copy_header=True) 

1479 # only the min value should be different 

1480 match_headers_keys( 

1481 result, 

1482 img_4d_mni_tr2, 

1483 except_keys=["cal_min", "cal_max"], 

1484 ) 

1485 # min value should be 0 in the result 

1486 assert result.header["cal_min"] == 0 

1487 # max value should be 1 in the result 

1488 assert result.header["cal_max"] == 1 

1489 

1490 

1491def test_binarize_img_no_userwarning(img_4d_rand_eye): 

1492 # Test that a UserWarning is not thrown for a float64 img 

1493 with warnings.catch_warnings(): 

1494 warnings.simplefilter("error", category=UserWarning) 

1495 binarize_img(img_4d_rand_eye) 

1496 

1497 

1498@pytest.mark.parametrize( 

1499 "func, input_img", 

1500 [ 

1501 (binarize_img, "img_4d_mni_tr2"), 

1502 (crop_img, "img_4d_mni_tr2"), 

1503 (mean_img, "img_4d_mni_tr2"), 

1504 (threshold_img, "img_4d_mni_tr2"), 

1505 ], 

1506) 

1507def test_warning_copy_header_false(request, func, input_img): 

1508 # Use the request fixture to get the actual fixture value 

1509 actual_input_img = request.getfixturevalue(input_img) 

1510 with pytest.warns(FutureWarning, match="From release 0.13.0 onwards*"): 

1511 if func is threshold_img: 

1512 func(actual_input_img, threshold=0.5, copy_header=False) 

1513 else: 

1514 func(actual_input_img, copy_header=False) 

1515 

1516 

1517def test_clean_img(affine_eye, shape_3d_default, rng): 

1518 data = rng.standard_normal(size=(10, 10, 10, 100)) + 0.5 

1519 data_flat = data.T.reshape(100, -1) 

1520 data_img = Nifti1Image(data, affine_eye) 

1521 

1522 with pytest.raises(ValueError, match="t_r.*must be specified"): 

1523 clean_img(data_img, t_r=None, low_pass=0.1) 

1524 

1525 data_img_ = clean_img( 

1526 data_img, detrend=True, standardize=False, low_pass=0.1, t_r=1.0 

1527 ) 

1528 data_flat_ = signal.clean( 

1529 data_flat, detrend=True, standardize=False, low_pass=0.1, t_r=1.0 

1530 ) 

1531 

1532 assert_almost_equal(get_data(data_img_).T.reshape(100, -1), data_flat_) 

1533 # if NANs 

1534 data[:, 9, 9] = np.nan 

1535 # if infinity 

1536 data[:, 5, 5] = np.inf 

1537 nan_img = Nifti1Image(data, affine_eye) 

1538 

1539 clean_im = clean_img(nan_img, ensure_finite=True) 

1540 

1541 assert np.any(np.isfinite(get_data(clean_im))) 

1542 

1543 # test_clean_img_passing_nifti2image 

1544 data_img_nifti2 = Nifti2Image(data, affine_eye) 

1545 

1546 clean_img( 

1547 data_img_nifti2, detrend=True, standardize=False, low_pass=0.1, t_r=1.0 

1548 ) 

1549 

1550 # if mask_img 

1551 img, mask_img = generate_fake_fmri(shape=shape_3d_default, length=10) 

1552 

1553 data_img_mask_ = clean_img(img, mask_img=mask_img) 

1554 

1555 # Checks that output with full mask and without is equal 

1556 data_img_ = clean_img(img) 

1557 

1558 assert_almost_equal(get_data(data_img_), get_data(data_img_mask_)) 

1559 

1560 

1561def test_clean_img_surface(surf_img_2d, surf_img_1d, surf_mask_1d) -> None: 

1562 """Test clean on surface image. 

1563 

1564 - check that clean returns image of same shape, geometry 

1565 but different data 

1566 - with mask should only clean the included vertices 

1567 - 1D image should raise error. 

1568 - check sample mask can be passed as a kwarg and used correctly 

1569 """ 

1570 length = 50 

1571 imgs = surf_img_2d(length) 

1572 

1573 cleaned_img = clean_img( 

1574 imgs, detrend=True, standardize=False, low_pass=0.1, t_r=1.0 

1575 ) 

1576 

1577 assert cleaned_img.shape == imgs.shape 

1578 assert_polymesh_equal(cleaned_img.mesh, imgs.mesh) 

1579 with pytest.raises(ValueError, match="not equal"): 

1580 assert_surface_image_equal(cleaned_img, imgs) 

1581 

1582 cleaned_img_with_mask = clean_img( 

1583 imgs, 

1584 detrend=True, 

1585 standardize=False, 

1586 low_pass=0.1, 

1587 t_r=1.0, 

1588 mask_img=surf_mask_1d, 

1589 ) 

1590 with pytest.raises(ValueError, match="not equal"): 

1591 assert_surface_image_equal(cleaned_img_with_mask, cleaned_img) 

1592 

1593 # Checks that output with full mask and without is equal 

1594 full_mask = new_img_like( 

1595 surf_mask_1d, 

1596 data={k: np.ones(v.shape) for k, v in surf_mask_1d.data.parts.items()}, 

1597 ) 

1598 cleaned_img_with_full_mask = clean_img( 

1599 imgs, 

1600 detrend=True, 

1601 standardize=False, 

1602 low_pass=0.1, 

1603 t_r=1.0, 

1604 mask_img=full_mask, 

1605 ) 

1606 assert_surface_image_equal(cleaned_img, cleaned_img_with_full_mask) 

1607 

1608 # 1D fails 

1609 with pytest.raises(ValueError, match="should be 2D"): 

1610 clean_img(surf_img_1d, detrend=True) 

1611 

1612 sample_mask = np.arange(length - 1) 

1613 

1614 # check sample mask can be passed as a kwarg and used correctly 

1615 cleaned_img = clean_img( 

1616 imgs, 

1617 detrend=True, 

1618 standardize=False, 

1619 low_pass=0.1, 

1620 t_r=1.0, 

1621 clean__sample_mask=sample_mask, 

1622 ) 

1623 assert cleaned_img.shape[-1] == length - 1 

1624 

1625 

1626@pytest.mark.parametrize("create_files", [True, False]) 

1627def test_largest_cc_img(create_files, tmp_path): 

1628 """Check the extraction of the largest connected component, for niftis. 

1629 

1630 Similar to smooth_img tests for largest connected_component_img, here also 

1631 only the added features for largest_connected_component are tested. 

1632 """ 

1633 # Test whether dimension of 3Dimg and list of 3Dimgs are kept. 

1634 img1, img2, shapes = _make_largest_cc_img_test_data() 

1635 

1636 imgs = testing.write_imgs_to_path( 

1637 img1, img2, file_path=tmp_path, create_files=create_files 

1638 ) 

1639 # List of images as input 

1640 out = largest_connected_component_img(imgs) 

1641 

1642 assert isinstance(out, list) 

1643 assert len(out) == 2 

1644 for o, s in zip(out, shapes): 

1645 assert o.shape == (s) 

1646 

1647 # Single image as input 

1648 out = largest_connected_component_img(imgs[0]) 

1649 

1650 assert isinstance(out, Nifti1Image) 

1651 assert out.shape == (shapes[0]) 

1652 

1653 

1654@pytest.mark.parametrize("create_files", [True, False]) 

1655def test_largest_cc_img_non_native_endian_type(create_files, tmp_path): 

1656 # Test whether dimension of 3Dimg and list of 3Dimgs are kept. 

1657 img1, img2, shapes = _make_largest_cc_img_test_data() 

1658 

1659 # tests adapted to non-native endian data dtype 

1660 img1_change_dtype = Nifti1Image( 

1661 get_data(img1).astype(">f8"), affine=img1.affine 

1662 ) 

1663 img2_change_dtype = Nifti1Image( 

1664 get_data(img2).astype(">f8"), affine=img2.affine 

1665 ) 

1666 

1667 imgs = testing.write_imgs_to_path( 

1668 img1_change_dtype, 

1669 img2_change_dtype, 

1670 file_path=tmp_path, 

1671 create_files=create_files, 

1672 ) 

1673 # List of images as input 

1674 out = largest_connected_component_img(imgs) 

1675 

1676 assert isinstance(out, list) 

1677 assert len(out) == 2 

1678 for o, s in zip(out, shapes): 

1679 assert o.shape == (s) 

1680 

1681 # Single image as input 

1682 out = largest_connected_component_img(imgs[0]) 

1683 

1684 assert isinstance(out, Nifti1Image) 

1685 assert out.shape == (shapes[0]) 

1686 

1687 # Test the output with native and without native 

1688 out_native = largest_connected_component_img(img1) 

1689 

1690 out_non_native = largest_connected_component_img(img1_change_dtype) 

1691 assert_equal(get_data(out_native), get_data(out_non_native)) 

1692 

1693 

1694def test_largest_cc_img_error(shape_3d_default): 

1695 # Test whether 4D Nifti throws the right error. 

1696 img_4D = generate_fake_fmri(shape_3d_default) 

1697 

1698 with pytest.raises(DimensionError, match="dimension"): 

1699 largest_connected_component_img(img_4D) 

1700 

1701 

1702def test_new_img_like_mgh_image(affine_eye, shape_3d_default): 

1703 data = np.zeros(shape_3d_default, dtype=np.uint8) 

1704 niimg = MGHImage(dataobj=data, affine=affine_eye) 

1705 

1706 new_img_like(niimg, data.astype(float), niimg.affine, copy_header=True) 

1707 

1708 

1709@pytest.mark.parametrize("image", [MGHImage, AnalyzeImage]) 

1710def test_new_img_like_boolean_data(affine_eye, image, shape_3d_default, rng): 

1711 """Checks defaulting boolean input data to np.uint8 dtype is valid \ 

1712 forencoding with nibabel image classes MGHImage and AnalyzeImage. 

1713 """ 

1714 data = rng.standard_normal(shape_3d_default).astype("uint8") 

1715 in_img = image(dataobj=data, affine=affine_eye) 

1716 

1717 out_img = new_img_like(in_img, data=in_img.get_fdata() > 0.5) 

1718 

1719 assert get_data(out_img).dtype == "uint8" 

1720 

1721 

1722def test_clean_img_sample_mask(img_4d_rand_eye, shape_4d_default): 

1723 """Check sample mask can be passed as a kwarg and used correctly.""" 

1724 length = shape_4d_default[3] 

1725 confounds = _basic_confounds(length) 

1726 # exclude last time point 

1727 sample_mask = np.arange(length - 1) 

1728 

1729 img = clean_img( 

1730 img_4d_rand_eye, 

1731 confounds=confounds, 

1732 clean__sample_mask=sample_mask, 

1733 ) 

1734 assert img.shape == (*shape_4d_default[:3], length - 1) 

1735 

1736 

1737def test_clean_img_sample_mask_mask_img(shape_3d_default): 

1738 """Check sample_mask and mask_img can be correctly used together.""" 

1739 length = 10 

1740 confounds = _basic_confounds(length) 

1741 img_4d, mask_img = generate_fake_fmri( 

1742 shape=shape_3d_default, length=length 

1743 ) 

1744 

1745 # exclude last time point 

1746 sample_mask = np.arange(length - 1) 

1747 

1748 # test with sample mask 

1749 img = clean_img( 

1750 img_4d, 

1751 confounds=confounds, 

1752 mask_img=mask_img, 

1753 clean__sample_mask=sample_mask, 

1754 ) 

1755 assert img.shape == (*shape_3d_default, length - 1) 

1756 

1757 

1758def test_concat_niimgs_errors(affine_eye, shape_3d_default): 

1759 img1 = Nifti1Image(np.ones(shape_3d_default), affine_eye) 

1760 img2 = Nifti1Image(np.ones(shape_3d_default), 2 * affine_eye) 

1761 img4d = Nifti1Image(np.ones((*shape_3d_default, 2)), affine_eye) 

1762 

1763 # check error for non-forced but necessary resampling 

1764 with pytest.raises(ValueError, match="Field of view of image"): 

1765 concat_imgs([img1, img2], auto_resample=False) 

1766 

1767 # Regression test for #601. 

1768 # Dimensionality of first image was not checked properly. 

1769 _dimension_error_msg = ( 

1770 "Input data has incompatible dimensionality: " 

1771 "Expected dimension is 4D and you provided " 

1772 "a list of 4D images \\(5D\\)" 

1773 ) 

1774 with pytest.raises(DimensionError, match=_dimension_error_msg): 

1775 concat_imgs([img4d], ensure_ndim=4) 

1776 

1777 with pytest.raises(DimensionError, match=_dimension_error_msg): 

1778 concat_imgs([img1, img4d]) 

1779 

1780 img5d = Nifti1Image(np.ones((2, 2, 2, 2, 2)), affine_eye) 

1781 with pytest.raises( 

1782 TypeError, 

1783 match="Concatenated images must be 3D or 4D. " 

1784 "You gave a list of 5D images", 

1785 ): 

1786 concat_imgs([img5d, img5d]) 

1787 

1788 

1789def test_concat_niimgs(affine_eye, tmp_path): 

1790 # create images different in affine and 3D/4D shape 

1791 shape = (10, 11, 12) 

1792 img1 = Nifti1Image(np.ones(shape), affine_eye) 

1793 img2 = Nifti1Image(np.zeros(shape), affine_eye) 

1794 

1795 shape2 = (12, 11, 10) 

1796 img1b = Nifti1Image(np.ones(shape2), affine_eye) 

1797 

1798 shape3 = (11, 22, 33) 

1799 img1c = Nifti1Image(np.ones(shape3), affine_eye) 

1800 

1801 # check basic concatenation with equal shape/affine 

1802 # verbose for coverage 

1803 concatenated = concat_imgs((img1, img2, img1), verbose=1) 

1804 

1805 # smoke-test auto_resample 

1806 concatenated = concat_imgs((img1, img1b, img1c), auto_resample=True) 

1807 assert concatenated.shape == (*img1.shape, 3) 

1808 

1809 # test list of 4D niimgs as input 

1810 img1.to_filename(tmp_path / "1.nii") 

1811 img2.to_filename(tmp_path / "2.nii") 

1812 concatenated = concat_imgs(tmp_path / "*") 

1813 assert_array_equal(get_data(concatenated)[..., 0], get_data(img1)) 

1814 assert_array_equal(get_data(concatenated)[..., 1], get_data(img2)) 

1815 

1816 

1817def test_concat_niimg_dtype(affine_eye): 

1818 shape = [2, 3, 4] 

1819 vols = [ 

1820 Nifti1Image(np.zeros([*shape, n_scans]).astype(np.int16), affine_eye) 

1821 for n_scans in [1, 5] 

1822 ] 

1823 nimg = concat_imgs(vols) 

1824 assert get_data(nimg).dtype == np.float32 

1825 nimg = concat_imgs(vols, dtype=None) 

1826 assert get_data(nimg).dtype == np.int16 

1827 

1828 

1829def test_concat_imgs_surface(surf_img_2d): 

1830 """Check concat_imgs returns a single SurfaceImage. 

1831 

1832 Output must have as many samples as the sum of samples in the input. 

1833 """ 

1834 img = concat_imgs([surf_img_2d(3), surf_img_2d(5)], dtype=np.float16) 

1835 assert img.shape == (9, 8) 

1836 for value in img.data.parts.values(): 

1837 assert value.ndim == 2 

1838 assert value.dtype == np.float16 

1839 

1840 

1841def nifti_generator(buffer): 

1842 for _ in range(10): 

1843 buffer.append(_img_3d_rand()) 

1844 yield buffer[-1] 

1845 

1846 

1847def test_iterator_generator(img_3d_rand_eye): 

1848 # Create a list of random images 

1849 list_images = [img_3d_rand_eye for _ in range(10)] 

1850 cc = concat_imgs(list_images) 

1851 assert cc.shape[-1] == 10 

1852 assert_array_almost_equal(get_data(cc)[..., 0], get_data(list_images[0])) 

1853 

1854 # Same with iteration 

1855 i = iter_img(list_images) 

1856 cc = concat_imgs(i) 

1857 assert cc.shape[-1] == 10 

1858 assert_array_almost_equal(get_data(cc)[..., 0], get_data(list_images[0])) 

1859 

1860 # Now, a generator 

1861 b = [] 

1862 g = nifti_generator(b) 

1863 cc = concat_imgs(g) 

1864 assert cc.shape[-1] == 10 

1865 assert len(b) == 10 

1866 

1867 

1868def test_copy_img(): 

1869 with pytest.raises(ValueError, match="Input value is not an image"): 

1870 copy_img(3) 

1871 

1872 

1873def test_copy_img_side_effect(img_4d_ones_eye): 

1874 hash1 = joblib.hash(img_4d_ones_eye) 

1875 copy_img(img_4d_ones_eye) 

1876 hash2 = joblib.hash(img_4d_ones_eye) 

1877 assert hash1 == hash2