@@ -411,6 +411,113 @@ def test_get_data(self):
411
411
assert_false (rt_img .get_data () is out_data )
412
412
assert_array_equal (rt_img .get_data (), in_data )
413
413
414
+ def test_slicer (self ):
415
+ img_klass = self .image_class
416
+ in_data_template = np .arange (240 , dtype = np .int16 )
417
+ base_affine = np .eye (4 )
418
+ t_axis = None
419
+ for dshape in ((4 , 5 , 6 , 2 ), # Time series
420
+ (8 , 5 , 6 )): # Volume
421
+ in_data = in_data_template .copy ().reshape (dshape )
422
+ img = img_klass (in_data , base_affine .copy ())
423
+
424
+ # Detect time axis on first loop (4D image)
425
+ if t_axis is None :
426
+ t_axis = 3 if img ._spatial_dims .start == 0 else 0
427
+
428
+ assert_true (hasattr (img .slicer , '__getitem__' ))
429
+ if not img_klass .makeable :
430
+ assert_raises (NotImplementedError , img .slicer [:])
431
+ continue
432
+ # Note spatial zooms are always first 3, even when
433
+ spatial_zooms = img .header .get_zooms ()[:3 ]
434
+
435
+ # Down-sample with [::2, ::2, ::2] along spatial dimensions
436
+ sliceobj = [slice (None )] * len (dshape )
437
+ sliceobj [img ._spatial_dims ] = [slice (None , None , 2 )] * 3
438
+ downsampled_img = img .slicer [tuple (sliceobj )]
439
+ assert_array_equal (downsampled_img .header .get_zooms ()[:3 ],
440
+ np .array (spatial_zooms ) * 2 )
441
+
442
+ # Check newaxis errors
443
+ if t_axis == 3 :
444
+ with assert_raises (IndexError ):
445
+ img .slicer [None ]
446
+ elif len (img .shape ) == 4 :
447
+ with assert_raises (IndexError ):
448
+ img .slicer [None ]
449
+ # 4D Minc to 3D
450
+ assert_equal (img .slicer [0 ].shape , img .shape [1 :])
451
+ else :
452
+ # 3D Minc to 4D
453
+ assert_equal (img .slicer [None ].shape , (1 ,) + img .shape )
454
+ # Axes 1 and 2 are always spatial
455
+ with assert_raises (IndexError ):
456
+ img .slicer [:, None ]
457
+ with assert_raises (IndexError ):
458
+ img .slicer [:, :, None ]
459
+ if t_axis == 0 :
460
+ with assert_raises (IndexError ):
461
+ img .slicer [:, :, :, None ]
462
+ elif len (img .shape ) == 4 :
463
+ # Reorder non-spatial axes
464
+ assert_equal (img .slicer [:, :, :, None ].shape , img .shape [:3 ] + (1 ,) + img .shape [3 :])
465
+ else :
466
+ # 3D Analyze/NIfTI/MGH to 4D
467
+ assert_equal (img .slicer [:, :, :, None ].shape , img .shape + (1 ,))
468
+ if len (img .shape ) == 3 :
469
+ # Slices exceed dimensions
470
+ with assert_raises (IndexError ):
471
+ img .slicer [:, :, :, :, None ]
472
+ else :
473
+ assert_equal (img .slicer [:, :, :, :, None ].shape , img .shape + (1 ,))
474
+
475
+ # Crop by one voxel in each dimension
476
+ if len (img .shape ) == 3 or t_axis == 3 :
477
+ sliced_i = img .slicer [1 :]
478
+ sliced_j = img .slicer [:, 1 :]
479
+ sliced_k = img .slicer [:, :, 1 :]
480
+ sliced_ijk = img .slicer [1 :, 1 :, 1 :]
481
+ else :
482
+ # 4D Minc
483
+ sliced_i = img .slicer [:, 1 :]
484
+ sliced_j = img .slicer [:, :, 1 :]
485
+ sliced_k = img .slicer [:, :, :, 1 :]
486
+ sliced_ijk = img .slicer [:, 1 :, 1 :, 1 :]
487
+
488
+ # No scaling change
489
+ assert_array_equal (sliced_i .affine [:3 , :3 ], img .affine [:3 , :3 ])
490
+ assert_array_equal (sliced_j .affine [:3 , :3 ], img .affine [:3 , :3 ])
491
+ assert_array_equal (sliced_k .affine [:3 , :3 ], img .affine [:3 , :3 ])
492
+ assert_array_equal (sliced_ijk .affine [:3 , :3 ], img .affine [:3 , :3 ])
493
+ # Translation
494
+ assert_array_equal (sliced_i .affine [:, 3 ], [1 , 0 , 0 , 1 ])
495
+ assert_array_equal (sliced_j .affine [:, 3 ], [0 , 1 , 0 , 1 ])
496
+ assert_array_equal (sliced_k .affine [:, 3 ], [0 , 0 , 1 , 1 ])
497
+ assert_array_equal (sliced_ijk .affine [:, 3 ], [1 , 1 , 1 , 1 ])
498
+
499
+ # No change to affines with upper-bound slices
500
+ assert_array_equal (img .slicer [:1 , :1 , :1 ].affine , img .affine )
501
+
502
+ # Check data is consistent with slicing numpy arrays
503
+ slice_elems = (None , Ellipsis , 0 , 1 , - 1 , slice (None ), slice (1 ),
504
+ slice (- 1 ), slice (1 , - 1 ))
505
+ for n_elems in range (6 ):
506
+ for _ in range (10 ):
507
+ sliceobj = tuple (
508
+ np .random .choice (slice_elems , n_elems ).tolist ())
509
+ try :
510
+ sliced_img = img .slicer [sliceobj ]
511
+ except IndexError :
512
+ # Only checking valid slices
513
+ pass
514
+ else :
515
+ sliced_data = in_data [sliceobj ]
516
+ assert_array_equal (sliced_data , sliced_img .get_data ())
517
+ assert_array_equal (sliced_data , sliced_img .dataobj )
518
+ assert_array_equal (sliced_data , img .dataobj [sliceobj ])
519
+ assert_array_equal (sliced_data , img .get_data ()[sliceobj ])
520
+
414
521
def test_api_deprecations (self ):
415
522
416
523
class FakeImage (self .image_class ):
0 commit comments