Skip to content

Commit 5909001

Browse files
committed
Add writing capabilities for freesurfer triangle files
Produces files identical to freesurfer up to the point that nibabel reads. There is additional information after a freesurfer generated file, but nibabel reads equivalent data and PySurfer displays these files fine.
1 parent a9f37e7 commit 5909001

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

nibabel/freesurfer/io.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,32 @@ def read_geometry(filepath):
9292
coords = coords.astype(np.float) # XXX: due to mayavi bug on mac 32bits
9393
return coords, faces
9494

95+
def write_geometry(filepath, create_stamp, coords, faces):
96+
"""Write a triangular format Freesurfer surface mesh.
97+
98+
Parameters
99+
----------
100+
filepath : str
101+
Path to surface file
102+
create_stamp : str
103+
User/time stamp
104+
coords : numpy array
105+
nvtx x 3 array of vertex (x, y, z) coordinates
106+
faces : numpy array
107+
nfaces x 3 array of defining mesh triangles
108+
"""
109+
magic_bytes = np.array([255,255,254],dtype=np.uint8)
110+
with open(filepath, 'wb') as fobj:
111+
magic_bytes.tofile(fobj)
112+
fobj.write("%s\n\n" % create_stamp)
113+
114+
# On a Linux box, numpy uses opposite byte order to freesurfer
115+
np.int32(coords.shape[0]).byteswap().tofile(fobj)
116+
np.int32(faces.shape[0]).byteswap().tofile(fobj)
117+
118+
# Coerce types, just to be safe
119+
coords.astype('>f4').reshape(-1).tofile(fobj)
120+
faces.astype('>i4').reshape(-1).tofile(fobj)
95121

96122
def read_morph_data(filepath):
97123
"""Read a Freesurfer morphometry data file.

nibabel/freesurfer/tests/test_io.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import os
22
from os.path import join as pjoin
3+
import time
34

45
from nose.tools import assert_true
56
import numpy as np
67
from numpy.testing import assert_equal
78

8-
from .. import read_geometry, read_morph_data, read_annot, read_label
9+
from .. import read_geometry, read_morph_data, read_annot, read_label, write_geometry
910

1011

1112
have_freesurfer = True
@@ -36,6 +37,19 @@ def test_geometry():
3637
assert_equal(0, faces.min())
3738
assert_equal(coords.shape[0], faces.max() + 1)
3839

40+
# Test equivalence of freesurfer- and nibabel-generated triangular files
41+
# with respect to read_geometry()
42+
surf_path = os.tmpnam()
43+
create_stamp = "created by %s on %s" % (os.getlogin(), time.ctime())
44+
write_geometry(surf_path, create_stamp, coords, faces)
45+
46+
coords2, faces2 = read_geometry(surf_path)
47+
48+
# Remove
49+
os.unlink(surf_path)
50+
51+
assert_equal(coords, coords2)
52+
assert_equal(faces, faces2)
3953

4054
@freesurfer_test
4155
def test_morph_data():

0 commit comments

Comments
 (0)