Skip to content

Commit ace3e4e

Browse files
committed
ENH: Add nib-roi command to crop (maybe flip) axes
1 parent 2f918b1 commit ace3e4e

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

nibabel/cmdline/roi.py

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import argparse
2+
import nibabel as nb
3+
4+
5+
def lossless_slice(img, slicers):
6+
if not nb.imageclasses.spatial_axes_first(img):
7+
raise ValueError("Cannot slice an image that is not known to have spatial axes first")
8+
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)
14+
return roi_img
15+
16+
17+
def parse_slice(crop, allow_step=True):
18+
if crop is None:
19+
return slice(None)
20+
start, stop, *extra = [int(val) if val else None for val in crop.split(":")]
21+
if len(extra) > 1:
22+
raise ValueError(f"Cannot parse specification: {crop}")
23+
if extra and not allow_step:
24+
raise ValueError(f"Step entry not permitted: {crop}")
25+
26+
step = extra[0] if extra else None
27+
if step not in (1, -1, None):
28+
raise ValueError(f"Downsampling is not supported: {crop}")
29+
30+
return slice(start, stop, step)
31+
32+
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.")
36+
parser.add_argument("-i", metavar="I1:I2[:-1]",
37+
help="Start/stop [flip] along first axis (0-indexed)")
38+
parser.add_argument("-j", metavar="J1:J2[:-1]",
39+
help="Start/stop [flip] along second axis (0-indexed)")
40+
parser.add_argument("-k", metavar="K1:K2[:-1]",
41+
help="Start/stop [flip] along third axis (0-indexed)")
42+
parser.add_argument("-t", metavar="T1:T2", help="Start/stop along fourth axis (0-indexed)")
43+
parser.add_argument("in_file", help="Image file to crop")
44+
parser.add_argument("out_file", help="Output file name")
45+
46+
opts = parser.parse_args()
47+
48+
try:
49+
islice = parse_slice(opts.i)
50+
jslice = parse_slice(opts.j)
51+
kslice = parse_slice(opts.k)
52+
tslice = parse_slice(opts.t, allow_step=False)
53+
except ValueError as err:
54+
print(f"Could not parse input arguments. Reason follows.\n{err}")
55+
return 1
56+
57+
img = nb.load(opts.in_file)
58+
try:
59+
sliced_img = lossless_slice(img, (islice, jslice, kslice, tslice)[:img.ndim])
60+
except:
61+
print("Could not slice image. Full traceback follows.")
62+
raise
63+
nb.save(sliced_img, opts.out_file)
64+
return 0

setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ console_scripts =
7777
nib-nifti-dx=nibabel.cmdline.nifti_dx:main
7878
nib-tck2trk=nibabel.cmdline.tck2trk:main
7979
nib-trk2tck=nibabel.cmdline.trk2tck:main
80+
nib-roi=nibabel.cmdline.roi:main
8081
parrec2nii=nibabel.cmdline.parrec2nii:main
8182

8283
[options.package_data]

0 commit comments

Comments
 (0)