1
+ import sys
2
+ import os
1
3
import argparse
2
4
import nibabel as nb
3
5
@@ -6,11 +8,13 @@ def lossless_slice(img, slicers):
6
8
if not nb .imageclasses .spatial_axes_first (img ):
7
9
raise ValueError ("Cannot slice an image that is not known to have spatial axes first" )
8
10
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 )
14
18
return roi_img
15
19
16
20
@@ -20,7 +24,7 @@ def parse_slice(crop, allow_step=True):
20
24
start , stop , * extra = [int (val ) if val else None for val in crop .split (":" )]
21
25
if len (extra ) > 1 :
22
26
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 ) :
24
28
raise ValueError (f"Step entry not permitted: { crop } " )
25
29
26
30
step = extra [0 ] if extra else None
@@ -30,9 +34,19 @@ def parse_slice(crop, allow_step=True):
30
34
return slice (start , stop , step )
31
35
32
36
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__ )
36
50
parser .add_argument ("-i" , metavar = "I1:I2[:-1]" ,
37
51
help = "Start/stop [flip] along first axis (0-indexed)" )
38
52
parser .add_argument ("-j" , metavar = "J1:J2[:-1]" ,
@@ -43,7 +57,7 @@ def main():
43
57
parser .add_argument ("in_file" , help = "Image file to crop" )
44
58
parser .add_argument ("out_file" , help = "Output file name" )
45
59
46
- opts = parser .parse_args ()
60
+ opts = parser .parse_args (args = sanitize ( args ) )
47
61
48
62
try :
49
63
islice = parse_slice (opts .i )
@@ -54,10 +68,20 @@ def main():
54
68
print (f"Could not parse input arguments. Reason follows.\n { err } " )
55
69
return 1
56
70
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
+
58
82
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 :
61
85
print ("Could not slice image. Full traceback follows." )
62
86
raise
63
87
nb .save (sliced_img , opts .out_file )
0 commit comments