Skip to content

Commit 4b6ca81

Browse files
authored
Merge pull request #720 from soichih/master
FIX: Accept any valid delimiters/EOF markers in TCK files
2 parents ad6b890 + 486bbb2 commit 4b6ca81

File tree

4 files changed

+40
-37
lines changed

4 files changed

+40
-37
lines changed

nibabel/cifti2/cifti2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def rgba(self):
287287
return (self.red, self.green, self.blue, self.alpha)
288288

289289
def _to_xml_element(self):
290-
if self.label is '':
290+
if self.label == '':
291291
raise Cifti2HeaderError('Label needs a name')
292292
try:
293293
v = int(self.key)

nibabel/streamlines/tck.py

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -390,18 +390,14 @@ def _read(cls, fileobj, header, buffer_size=4):
390390
buffer_size = int(buffer_size * MEGABYTE)
391391
buffer_size += coordinate_size - (buffer_size % coordinate_size)
392392

393-
# Markers for streamline end and file end
394-
fiber_marker = cls.FIBER_DELIMITER.astype(dtype).tostring()
395-
eof_marker = cls.EOF_DELIMITER.astype(dtype).tostring()
396-
397393
with Opener(fileobj) as f:
398394
start_position = f.tell()
399395

400396
# Set the file position at the beginning of the data.
401397
f.seek(header["_offset_data"], os.SEEK_SET)
402398

403399
eof = False
404-
buffs = []
400+
leftover = np.empty((0, 3), dtype='<f4')
405401
n_streams = 0
406402

407403
while not eof:
@@ -411,37 +407,36 @@ def _read(cls, fileobj, header, buffer_size=4):
411407
if eof:
412408
buff = buff[:n_read]
413409

414-
buffs.append(buff)
415-
416-
# Make sure we've read enough to find a streamline delimiter.
417-
if fiber_marker not in buff:
418-
# If we've read the whole file, then fail.
419-
if eof:
420-
# Could have minimal buffering, and have read only the
421-
# EOF delimiter
422-
buffs = [bytearray().join(buffs)]
423-
if not buffs[0] == eof_marker:
424-
raise DataError(
425-
"Cannot find a streamline delimiter. This file"
426-
" might be corrupted.")
427-
else:
428-
# Otherwise read a bit more.
429-
continue
430-
431-
all_parts = bytearray().join(buffs).split(fiber_marker)
432-
point_parts, buffs = all_parts[:-1], all_parts[-1:]
433-
point_parts = [p for p in point_parts if p != b'']
434-
435-
for point_part in point_parts:
436-
# Read floats.
437-
pts = np.frombuffer(point_part, dtype=dtype)
438-
# Convert data to little-endian if needed.
439-
yield pts.astype('<f4', copy=False).reshape([-1, 3])
440-
441-
n_streams += len(point_parts)
442-
443-
if not buffs[-1] == eof_marker:
444-
raise DataError("Expecting end-of-file marker 'inf inf inf'")
410+
raw_values = np.frombuffer(buff, dtype=dtype)
411+
412+
# Convert raw_values into a list of little-endian triples (for x,y,z coord)
413+
coords = raw_values.astype('<f4', copy=False).reshape((-1, 3))
414+
415+
# Find stream delimiter locations (all NaNs)
416+
delims = np.where(np.isnan(coords).all(axis=1))[0]
417+
418+
# Recover leftovers, which can't have delimiters in them
419+
if leftover.size:
420+
delims += leftover.shape[0]
421+
coords = np.vstack((leftover, coords))
422+
423+
begin = 0
424+
for delim in delims:
425+
pts = coords[begin:delim]
426+
if pts.size:
427+
yield pts
428+
n_streams += 1
429+
begin = delim + 1
430+
431+
# The rest becomes the new leftover.
432+
leftover = coords[begin:]
433+
434+
if not (leftover.shape == (1, 3) and np.isinf(leftover).all()):
435+
if n_streams == 0:
436+
msg = "Cannot find a streamline delimiter. This file might be corrupted."
437+
else:
438+
msg = "Expecting end-of-file marker 'inf inf inf'"
439+
raise DataError(msg)
445440

446441
# In case the 'count' field was not provided.
447442
header[Field.NB_STREAMLINES] = n_streams

nibabel/streamlines/tests/test_tck.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def setup():
3232
"simple_big_endian.tck")
3333
# standard.tck contains only streamlines
3434
DATA['standard_tck_fname'] = pjoin(data_path, "standard.tck")
35+
DATA['matlab_nan_tck_fname'] = pjoin(data_path, "matlab_nan.tck")
3536

3637
DATA['streamlines'] = [np.arange(1 * 3, dtype="f4").reshape((1, 3)),
3738
np.arange(2 * 3, dtype="f4").reshape((2, 3)),
@@ -64,6 +65,13 @@ def test_load_simple_file(self):
6465
tck = TckFile(tractogram, header=hdr)
6566
assert_tractogram_equal(tck.tractogram, DATA['simple_tractogram'])
6667

68+
def test_load_matlab_nan_file(self):
69+
for lazy_load in [False, True]:
70+
tck = TckFile.load(DATA['matlab_nan_tck_fname'], lazy_load=lazy_load)
71+
streamlines = list(tck.tractogram.streamlines)
72+
assert_equal(len(streamlines), 1)
73+
assert_equal(streamlines[0].shape, (108, 3))
74+
6775
def test_writeable_data(self):
6876
data = DATA['simple_tractogram']
6977
for key in ('simple_tck_fname', 'simple_tck_big_endian_fname'):

nibabel/tests/data/matlab_nan.tck

1.62 KB
Binary file not shown.

0 commit comments

Comments
 (0)