Skip to content

Commit 97eb817

Browse files
committed
RF+TST - add constructor args, fixes, tests for trackvis class interface
1 parent 1f820ef commit 97eb817

File tree

2 files changed

+96
-8
lines changed

2 files changed

+96
-8
lines changed

nibabel/tests/test_trackvis.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
''' Testing trackvis module '''
22
from functools import partial
3-
import warnings
43

54
import numpy as np
65

@@ -503,3 +502,73 @@ def test_tv_class():
503502
assert_raises(tv.TrackvisFileError,
504503
tv.TrackvisFile,
505504
iter([]))
505+
506+
507+
def test_tvfile_io():
508+
# Test reading and writing tracks with file class
509+
out_f = BytesIO()
510+
ijk0 = np.arange(15).reshape((5,3)) / 2.0
511+
ijk1 = ijk0 + 20
512+
vx_streams = [(ijk0, None, None), (ijk1, None, None)]
513+
vxmm_streams = [(ijk0 * [[2,3,4]], None, None),
514+
(ijk1 * [[2,3,4]], None, None)]
515+
# Roundtrip basic
516+
tvf = tv.TrackvisFile(vxmm_streams)
517+
tvf.to_file(out_f)
518+
out_f.seek(0)
519+
tvf2 = tv.TrackvisFile.from_file(out_f)
520+
assert_equal(tvf2.filename, None)
521+
assert_true(streamlist_equal(vxmm_streams, tvf2.streamlines))
522+
assert_equal(tvf2.points_space, None)
523+
# Voxel points_space
524+
tvf = tv.TrackvisFile(vx_streams, points_space='voxel')
525+
out_f.seek(0)
526+
# No voxel size - error
527+
assert_raises(tv.HeaderError, tvf.to_file, out_f)
528+
out_f.seek(0)
529+
# With voxel size, no error, roundtrip works
530+
tvf.header['voxel_size'] = [2,3,4]
531+
tvf.to_file(out_f)
532+
out_f.seek(0)
533+
tvf2 = tv.TrackvisFile.from_file(out_f, points_space='voxel')
534+
assert_true(streamlist_equal(vx_streams, tvf2.streamlines))
535+
assert_equal(tvf2.points_space, 'voxel')
536+
out_f.seek(0)
537+
# Also with affine specified
538+
tvf = tv.TrackvisFile(vx_streams, points_space='voxel',
539+
affine=np.diag([2,3,4,1]))
540+
tvf.to_file(out_f)
541+
out_f.seek(0)
542+
tvf2 = tv.TrackvisFile.from_file(out_f, points_space='voxel')
543+
assert_true(streamlist_equal(vx_streams, tvf2.streamlines))
544+
# Fancy affine test
545+
fancy_affine = np.array([[0., -2, 0, 10],
546+
[3, 0, 0, 20],
547+
[0, 0, 4, 30],
548+
[0, 0, 0, 1]])
549+
def f(pts): # from vx to mm
550+
pts = pts[:,[1,0,2]] * [[-2,3,4]] # apply zooms / reorder
551+
return pts + [[10,20,30]] # apply translations
552+
xyz0, xyz1 = f(ijk0), f(ijk1)
553+
fancy_rasmm_streams = [(xyz0, None, None), (xyz1, None, None)]
554+
# Roundtrip
555+
tvf = tv.TrackvisFile(fancy_rasmm_streams, points_space='rasmm')
556+
out_f.seek(0)
557+
# No affine
558+
assert_raises(tv.HeaderError, tvf.to_file, out_f)
559+
out_f.seek(0)
560+
# With affine set, no error, roundtrip works
561+
tvf.set_affine(fancy_affine, pos_vox=True, set_order=True)
562+
tvf.to_file(out_f)
563+
out_f.seek(0)
564+
tvf2 = tv.TrackvisFile.from_file(out_f, points_space='rasmm')
565+
assert_true(streamlist_equal(fancy_rasmm_streams, tvf2.streamlines))
566+
assert_equal(tvf2.points_space, 'rasmm')
567+
out_f.seek(0)
568+
# Also when affine given in init
569+
tvf = tv.TrackvisFile(fancy_rasmm_streams, points_space='rasmm',
570+
affine=fancy_affine)
571+
tvf.to_file(out_f)
572+
out_f.seek(0)
573+
tvf2 = tv.TrackvisFile.from_file(out_f, points_space='rasmm')
574+
assert_true(streamlist_equal(fancy_rasmm_streams, tvf2.streamlines))

nibabel/trackvis.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -752,37 +752,56 @@ class TrackvisFile(object):
752752
endianness : {None, '<', '>'}
753753
Set here explicit endianness if required. Endianness otherwise inferred
754754
from `streamlines`
755-
filename : None or str
755+
filename : None or str, optional
756756
filename
757+
points_space : {None, 'voxel', 'rasmm'}, optional
758+
Space in which streamline points are expressed in memory. Default
759+
(None) means streamlines contain points in trackvis *voxmm* space (voxel
760+
positions * voxel sizes). 'voxel' means points are in voxel space (and
761+
need to be multiplied by voxel size for saving in file). 'rasmm' mean
762+
the points are expressed in mm space according to the affine. See
763+
``read`` and ``write`` function docstrings for more detail.
764+
affine : None or (4,4) ndarray, optional
765+
Affine expressing relationship of voxels in an image to mm in RAS mm
766+
space. If 'points_space' is not None, you can use this to give the
767+
relationship between voxels, rasmm and voxmm space (above).
757768
'''
758769
def __init__(self,
759770
streamlines,
760771
mapping=None,
761772
endianness=None,
762-
filename=None):
773+
filename=None,
774+
points_space=None,
775+
affine = None,
776+
):
763777
try:
764778
n_streams = len(streamlines)
765779
except TypeError:
766780
raise TrackvisFileError('Need sequence for streamlines input')
767781
self.streamlines = streamlines
768782
if endianness is None:
769783
if n_streams > 0:
770-
endianness = endian_codes[streamlines[0].dtype.byteorder]
784+
pts0 = streamlines[0][0]
785+
endianness = endian_codes[pts0.dtype.byteorder]
771786
else:
772787
endianness = native_code
773788
self.header = _hdr_from_mapping(None, mapping, endianness)
774789
self.endianness = endianness
775790
self.filename = filename
791+
self.points_space = points_space
792+
if not affine is None:
793+
self.set_affine(affine, pos_vox=True, set_order=True)
776794

777795
@classmethod
778-
def from_file(klass, file_like):
779-
streamlines, header = read(file_like)
796+
def from_file(klass, file_like, points_space=None):
797+
streamlines, header = read(file_like, points_space=points_space)
780798
filename = (file_like if isinstance(file_like, basestring)
781799
else None)
782-
return klass(streamlines, header, None, filename)
800+
return klass(streamlines, header, None, filename, points_space)
783801

784802
def to_file(self, file_like):
785-
write(file_like, self.streamlines, self.header, self.endianness)
803+
write(file_like, self.streamlines, self.header, self.endianness,
804+
points_space=self.points_space)
786805
self.filename = (file_like if isinstance(file_like, basestring)
787806
else None)
788807

0 commit comments

Comments
 (0)