Skip to content

Commit 782bcad

Browse files
committed
Feature(cli.reslice): option to only reslice a subset of channels
1 parent c972433 commit 782bcad

File tree

3 files changed

+54
-13
lines changed

3 files changed

+54
-13
lines changed

nitorch/cli/registration/reslice/main.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,18 @@ def read_file(fname):
9696
o.channels = o.shape[-1]
9797
o.shape = o.shape[:3]
9898
o.affine = f.affine.float()
99+
100+
if options.channels is not None:
101+
channels = py.make_list(options.channels)
102+
channels = [
103+
list(c) if isinstance(c, range) else
104+
list(range(o.channels))[c] if isinstance(c, slice) else
105+
c for c in channels
106+
]
107+
if not all([isinstance(c, int) for c in channels]):
108+
raise ValueError('Channel list should be a list of integers')
109+
o.channels = len(channels)
110+
99111
return o
100112

101113
def read_affine(fname):
@@ -304,6 +316,16 @@ def build_from_target(affine, shape, smart=False):
304316
else:
305317
dat = io.volumes.loadf(file.fname, rand=False, **backend)
306318
opt_pull = opt_pull0
319+
if options.channels is not None:
320+
channels = py.make_list(options.channels)
321+
channels = [
322+
list(c) if isinstance(c, range) else
323+
list(range(dat.shape[-1]))[c] if isinstance(c, slice) else
324+
c for c in channels
325+
]
326+
if not all([isinstance(c, int) for c in channels]):
327+
raise ValueError('Channel list should be a list of integers')
328+
dat = dat[..., channels]
307329
dat = dat.reshape([*file.shape, file.channels])
308330
dat = utils.movedim(dat, -1, 0)
309331

@@ -328,5 +350,3 @@ def build_from_target(affine, shape, smart=False):
328350
io.volumes.save(dat, ofname, like=file.fname, affine=oaffine, dtype=options.dtype)
329351
else:
330352
io.volumes.savef(dat, ofname, like=file.fname, affine=oaffine, dtype=options.dtype)
331-
332-

nitorch/cli/registration/reslice/parser.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,29 @@
44
help = r"""[nitorch] Reslice volumes
55
66
usage:
7-
nitorch reslice *FILES <*TRF> FILE [-t FILE] [-o *FILE]
7+
nitorch reslice *FILES <*TRF> FILE [-t FILE] [-o *FILE]
88
[-i ORDER] [-b BND] [-p] [-x] [-cpu|gpu]
9-
9+
1010
<TRF> can take values (with additional options):
1111
-l, --linear Linear transform (i.e., affine matrix)
1212
-d, --displacement Dense or free-form displacement field
1313
-n, --order Order of the encoding splines (1)
1414
-u, --unit Unit/Space of the displacement (mm or [vox])
15-
-v, --velocity Diffeomorphic velocity field
15+
-v, --velocity Diffeomorphic velocity field
1616
[and JSON file of shooting parameters]
1717
If no JSON file, assume stationary velocity.
18-
18+
1919
Each of these transforms can be inverted by prepending 'i' in the
2020
short form or appending '-inverse' in the long form:
2121
-il, --linear-inverse Inverse of a linear transform
2222
-id, --displacement-inverse Inverse of a dense or ffd displacement field
2323
-iv, --velocity-inverse Inverse of a diffeomorphic velocity field
24-
25-
It is also possible to append a '2' to specify that the affine is the
24+
25+
It is also possible to append a '2' to specify that the affine is the
2626
square of the transform to apply (i.e., its square root will be applied):
2727
-l2, --linear-square Square of a linear transform
2828
-v2, --velocity-square Square of a diffeomorphic velocity field
29-
29+
3030
Other tags are:
3131
-t, --target Defines the target space.
3232
If not provided, minimal reslicing is performed.
@@ -36,12 +36,13 @@
3636
-b, --bound Boundary conditions (dct2)
3737
-x, --extrapolate Extrapolate out-of-bounds data (no)
3838
-v, --voxel-size Voxel size of the resliced space (default: from target)
39+
-c, --channels Channels to load. Can be a range start:stop:step (default: all)
3940
-dt, --dtype Output data type (default: from input)
4041
-cpu, -gpu Device to use (cpu)
41-
42+
4243
The output image is
4344
input_dat(inv(inpt_aff) o trf[0] o trf[1] o ... trf[-1] o target_aff)
44-
45+
4546
For example, if `autoreg` has been run to register 'mov.nii' to 'fix.nii'
4647
and has generated transforms 'affine.lta' and 'velocity.nii':
4748
- reslicing mov to fix:
@@ -178,6 +179,11 @@ def parse(args):
178179
while cli.next_isvalue(args):
179180
val, *args = args
180181
options.voxel_size.append(float(val))
182+
elif tag in ('-c', '--channels'):
183+
options.channels = []
184+
while cli.next_isvalue(args):
185+
val, *args = args
186+
options.channels.append(parse_range(val))
181187
elif tag in ('-dt', '--dtype'):
182188
cli.check_next_isvalue(args, tag)
183189
options.dtype, *args = args
@@ -197,3 +203,19 @@ def parse(args):
197203

198204
return options
199205

206+
207+
def parse_range(x):
208+
if ':' not in x:
209+
return int(x)
210+
x = x.split(':')
211+
if len(x) == 2:
212+
x = [*x, '']
213+
elif len(x) == 1:
214+
x = ['', *x, '']
215+
start, stop, step = x
216+
start = int(start or 0)
217+
step = int(step or 1)
218+
if stop == '':
219+
return slice(start, None, step)
220+
else:
221+
return range(start, int(stop), step)

nitorch/cli/registration/reslice/struct.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,4 @@ class Reslicer(Structure):
4545
dtype: str = None
4646
device: str = 'cpu'
4747
prefilter: bool = True
48-
49-
48+
channels: list = None

0 commit comments

Comments
 (0)