1+ import sys
2+ import os
13import argparse
24import nibabel as nb
35
@@ -6,11 +8,13 @@ def lossless_slice(img, slicers):
68 if not nb .imageclasses .spatial_axes_first (img ):
79 raise ValueError ("Cannot slice an image that is not known to have spatial axes first" )
810
9- roi_img = img .__class__ (
10- img .dataobj ._get_unscaled (slicers ),
11- affine = img .slicer .slice_affine (slicers ),
12- header = img .header )
13- roi_img .header .set_slope_inter (img .dataobj .slope , img .dataobj .inter )
11+ scaling = hasattr (img .dataobj , 'slope' )
12+
13+ data = img .dataobj ._get_unscaled (slicers ) if scaling else img .dataobj [slicers ]
14+ roi_img = img .__class__ (data , affine = img .slicer .slice_affine (slicers ), header = img .header )
15+
16+ if scaling :
17+ roi_img .header .set_slope_inter (img .dataobj .slope , img .dataobj .inter )
1418 return roi_img
1519
1620
@@ -20,7 +24,7 @@ def parse_slice(crop, allow_step=True):
2024 start , stop , * extra = [int (val ) if val else None for val in crop .split (":" )]
2125 if len (extra ) > 1 :
2226 raise ValueError (f"Cannot parse specification: { crop } " )
23- if extra and not allow_step :
27+ if not allow_step and extra and extra [ 0 ] not in ( 1 , None ) :
2428 raise ValueError (f"Step entry not permitted: { crop } " )
2529
2630 step = extra [0 ] if extra else None
@@ -30,9 +34,19 @@ def parse_slice(crop, allow_step=True):
3034 return slice (start , stop , step )
3135
3236
33- def main ():
34- parser = argparse .ArgumentParser (description = "Crop images to a region of interest" ,
35- epilog = "If a start or stop value is omitted, the start or end of the axis is assumed." )
37+ def sanitize (args ):
38+ # Argparse likes to treat "-1:..." as a flag
39+ return [f' { arg } ' if arg [0 ] == '-' and ":" in arg else arg
40+ for arg in args ]
41+
42+
43+ def main (args = None ):
44+ if args is None :
45+ args = sys .argv [1 :]
46+ parser = argparse .ArgumentParser (
47+ description = "Crop images to a region of interest" ,
48+ epilog = "If a start or stop value is omitted, the start or end of the axis is assumed." )
49+ parser .add_argument ('--version' , action = 'version' , version = nb .__version__ )
3650 parser .add_argument ("-i" , metavar = "I1:I2[:-1]" ,
3751 help = "Start/stop [flip] along first axis (0-indexed)" )
3852 parser .add_argument ("-j" , metavar = "J1:J2[:-1]" ,
@@ -43,7 +57,7 @@ def main():
4357 parser .add_argument ("in_file" , help = "Image file to crop" )
4458 parser .add_argument ("out_file" , help = "Output file name" )
4559
46- opts = parser .parse_args ()
60+ opts = parser .parse_args (args = sanitize ( args ) )
4761
4862 try :
4963 islice = parse_slice (opts .i )
@@ -54,10 +68,20 @@ def main():
5468 print (f"Could not parse input arguments. Reason follows.\n { err } " )
5569 return 1
5670
57- img = nb .load (opts .in_file )
71+ kwargs = {}
72+ if os .path .realpath (opts .in_file ) == os .path .realpath (opts .out_file ):
73+ kwargs ['mmap' ] = False
74+ img = nb .load (opts .in_file , ** kwargs )
75+
76+ slicers = (islice , jslice , kslice , tslice )[:img .ndim ]
77+ expected_shape = nb .fileslice .predict_shape (slicers , img .shape )
78+ if any (dim == 0 for dim in expected_shape ):
79+ print (f"Cannot take zero-length slices. Predicted shape { expected_shape } ." )
80+ return 1
81+
5882 try :
59- sliced_img = lossless_slice (img , ( islice , jslice , kslice , tslice )[: img . ndim ] )
60- except :
83+ sliced_img = lossless_slice (img , slicers )
84+ except Exception :
6185 print ("Could not slice image. Full traceback follows." )
6286 raise
6387 nb .save (sliced_img , opts .out_file )
0 commit comments