@@ -461,12 +461,118 @@ def from_image(klass, img):
461
461
klass .header_class .from_header (img .header ),
462
462
extra = img .extra .copy ())
463
463
464
+ @staticmethod
465
+ def requires_downsampling (idx ):
466
+ for slicer in idx :
467
+ if isinstance (slicer , slice ) and slicer .step not in (1 , None ):
468
+ return True
469
+ elif np .asanyarray (slicer ).shape != (1 ,):
470
+ return True
471
+
472
+ return False
473
+
474
+ def slice_afffine (self , idx ):
475
+ """ Retrieve affine for current image, if sliced by a given index
476
+
477
+ Applies scaling if down-sampling is applied, and adjusts the intercept
478
+ to account for any cropping.
479
+
480
+ Parameters
481
+ ----------
482
+ idx : numpy-compatible slice index
483
+
484
+ Returns
485
+ -------
486
+ affine : (4,4) ndarray
487
+ Affine with updated scale and intercept
488
+ """
489
+ # Expand ellipsis to retrieve bounds
490
+ if Ellipsis in idx :
491
+ i = idx .index (Ellipsis )
492
+ subslices = tuple (slice (None )
493
+ for _ in range (len (self .shape ) - len (idx .shape )))
494
+ idx = idx [:i ] + subslices + idx [i + 1 :]
495
+
496
+ origin = np .array ([[0 ], [0 ], [0 ], [1 ]])
497
+ scales = np .eye (3 , dtype = int )
498
+
499
+ for i , slicer in enumerate (idx [:3 ]):
500
+ new_origin [i ] = (slicer .start or 0 if isinstance (slicer , slice )
501
+ else np .asanyarray (slicer )[0 ])
502
+ if slicer .step is not None :
503
+ scales [i , i ] = slicer .step
504
+
505
+ affine = self .affine .copy ()
506
+ affine [:3 , :3 ] = scales .dot (self .affine [:3 , :3 ])
507
+ affine [:, [3 ]] = self .affine .dot (origin )
508
+ return affine
509
+
510
+ def slice (self , idx , allow_downsampling = False ):
511
+ """ Slice image to specification
512
+
513
+ Nibabel performs no spatial filtering, so, in order to avoid aliasing
514
+ data, only slices that specify cropping data are permitted.
515
+ If the image has been filtered, or aliasing is not a concern,
516
+ ``allow_downsampling=True`` will disable this check and permit more
517
+ complicated slices.
518
+
519
+ The image is resliced in the current orientation; no rotation or
520
+ resampling is performed.
521
+ The affine matrix is updated with the new intercept (and scales, if
522
+ down-sampling is used), so that all values are found at the same RAS
523
+ locations.
524
+
525
+ Slicing may include non-spatial dimensions.
526
+ However, this method does not currently adjust the repetition time in
527
+ the image header.
528
+
529
+ Parameters
530
+ ----------
531
+ idx : numpy-compatible slice index
532
+ allow_downsampling : bool (default: False)
533
+ Permit indices that specify down-sampling
534
+
535
+ Returns
536
+ -------
537
+ resliced_img : ``spatialimage``
538
+ Version of `img` with resliced data array and updated affine matrix
539
+ """
540
+ if not isinstance (idx , tuple ):
541
+ idx = (idx ,)
542
+
543
+ if not allow_downsampling and self .requires_downsampling (idx ):
544
+ raise IndexError ('slicing requires downsampling, please use '
545
+ 'img.slice(..., allow_downsampling=True) instead' )
546
+
547
+ # Get bounded data, allow numpy to complain about any indexing errors
548
+ dataobj = self .dataobj [slices ]
549
+ affine = self .slice_affine (idx )
550
+ return self .__class__ (dataobj , affine , self .header )
551
+
464
552
def __getitem__ (self , idx ):
465
- ''' No slicing or dictionary interface for images
466
- '''
467
- raise TypeError ("Cannot slice image objects; consider slicing image "
468
- "array data with `img.dataobj[slice]` or "
469
- "`img.get_data()[slice]`" )
553
+ """ Crop image to specified slices
554
+
555
+ The image is cropped in the current orientation; no rotation or
556
+ resampling is performed.
557
+ The affine matrix is updated with the new intercept, so that all
558
+ values are found at the same RAS locations.
559
+
560
+ Cropping may include non-spatial dimensions.
561
+
562
+ Down-sampling and other slicing operations may be performed with
563
+ ``img.slice(..., allow_downsampling=True)``.
564
+
565
+ Parameters
566
+ ----------
567
+ idx : tuple containing (slice(step=None), Ellipsis, array-like with size (1,))
568
+ numpy-compatible slice index describing cropping only
569
+
570
+ Returns
571
+ -------
572
+ cropped_img : ``spatialimage``
573
+ Version of `img` with cropped data array and updated affine matrix
574
+ """
575
+ return self .slice (idx )
470
576
471
577
def orthoview (self ):
472
578
"""Plot the image using OrthoSlicer3D
0 commit comments