@@ -330,21 +330,36 @@ class SpatialImage(DataobjImage):
330
330
_spatial_dims = slice (0 , 3 )
331
331
332
332
class Slicer (object ):
333
+ ''' Slicing interface that returns a new image with an updated affine
334
+ '''
333
335
def __init__ (self , img ):
334
336
self .img = img
335
337
336
- def __getitem__ (self , idx ):
337
- idx = self ._validate (idx )
338
- dataobj = self .img .dataobj [idx ]
339
- affine = self .img .slice_affine (idx )
340
- return self .img .__class__ (dataobj , affine , self .img .header )
338
+ def __getitem__ (self , slicer ):
339
+ klass = self .img .__class__
340
+ if not klass .makeable :
341
+ raise NotImplementedError (
342
+ "Cannot slice un-makeable image types" )
343
+
344
+ slicer = self .img ._check_slicing (self ._arr_to_slice (slicer ),
345
+ self .img .shape )
346
+ dataobj = self .img .dataobj [slicer ]
347
+ affine = self .img ._slice_affine (slicer )
348
+ return klass (dataobj .copy (), affine , self .img .header )
349
+
350
+ def _arr_to_slice (self , slicer ):
351
+ ''' Convert single item sequence indices to slices '''
352
+ if not isinstance (slicer , tuple ):
353
+ slicer = (slicer ,)
341
354
342
- def _validate (self , idx ):
343
- idx = canonical_slicers (idx , self .img .shape )
344
- for slicer in idx :
345
- if isinstance (slicer , int ):
346
- raise IndexError ("Cannot slice image with integer" )
347
- return idx
355
+ out = []
356
+ for subslicer in slicer :
357
+ arr = np .asarray (subslicer )
358
+ if arr .shape == (1 ,):
359
+ subslicer = slice (arr [0 ], arr [0 ] + 1 )
360
+ out .append (subslicer )
361
+
362
+ return tuple (out )
348
363
349
364
def __init__ (self , dataobj , affine , header = None ,
350
365
extra = None , file_map = None ):
@@ -482,22 +497,49 @@ def from_image(klass, img):
482
497
klass .header_class .from_header (img .header ),
483
498
extra = img .extra .copy ())
484
499
485
- def slice_affine (self , idx ):
500
+ def _check_slicing (self , slicer , return_spatial = False ):
501
+ ''' Canonicalize slicers and check for scalar indices in spatial dims
502
+
503
+ Parameters
504
+ ----------
505
+ slicer : object
506
+ something that can be used to slice an array as in
507
+ ``arr[sliceobj]``
508
+ return_spatial : bool
509
+ return only slices along spatial dimensions (x, y, z)
510
+
511
+ Returns
512
+ -------
513
+ slicer : object
514
+ Validated slicer object that will slice image's `dataobj`
515
+ without collapsing spatial dimensions
516
+ '''
517
+ slicer = canonical_slicers (slicer , self .shape )
518
+ spatial_slices = slicer [self ._spatial_dims ]
519
+ if any (not isinstance (subslicer , (slice , None ))
520
+ for subslicer in spatial_slices ):
521
+ raise IndexError ("Scalar indices disallowed in spatial dimensions; "
522
+ "Use `[x]` or `x:x+1`." )
523
+ return spatial_slices if return_spatial else slicer
524
+
525
+ def _slice_affine (self , slicer ):
486
526
""" Retrieve affine for current image, if sliced by a given index
487
527
488
528
Applies scaling if down-sampling is applied, and adjusts the intercept
489
529
to account for any cropping.
490
530
491
531
Parameters
492
532
----------
493
- idx : numpy-compatible slice index
533
+ slicer : object
534
+ something that can be used to slice an array as in
535
+ ``arr[sliceobj]``
494
536
495
537
Returns
496
538
-------
497
539
affine : (4,4) ndarray
498
540
Affine with updated scale and intercept
499
541
"""
500
- idx = canonical_slicers ( idx , self .shape , check_inds = False )[: 3 ]
542
+ slicer = self ._check_slicing ( slicer , return_spatial = True )
501
543
502
544
# Transform:
503
545
# sx 0 0 tx
@@ -506,14 +548,12 @@ def slice_affine(self, idx):
506
548
# 0 0 0 1
507
549
transform = np .eye (4 , dtype = int )
508
550
509
- for i , slicer in enumerate (idx ):
510
- if isinstance (slicer , slice ):
511
- if slicer .step == 0 :
551
+ for i , subslicer in enumerate (slicer ):
552
+ if isinstance (subslicer , slice ):
553
+ if subslicer .step == 0 :
512
554
raise ValueError ("slice step cannot be 0" )
513
- transform [i , i ] = slicer .step if slicer .step is not None else 1
514
- transform [i , 3 ] = slicer .start or 0
515
- elif isinstance (slicer , int ):
516
- transform [i , 3 ] = slicer
555
+ transform [i , i ] = subslicer .step if subslicer .step is not None else 1
556
+ transform [i , 3 ] = subslicer .start or 0
517
557
# If slicer is None, nothing to do
518
558
519
559
return self .affine .dot (transform )
0 commit comments