Skip to content

Commit ae183fb

Browse files
committed
Move Surface to utils
Surface was not imported in utils, which was breaking coord_to_label. I could not perform a relative import of io from utils (I do not understand that bug, but there were some other reports of similar issues with package modules named io on Google). Anyway, tests pass and docs build with the reorganization.
1 parent 26953a4 commit ae183fb

File tree

6 files changed

+126
-131
lines changed

6 files changed

+126
-131
lines changed

surfer/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
from io import Surface
21
from viz import Brain, TimeViewer
3-
from utils import verbose, set_log_level, set_log_file
2+
from utils import Surface, verbose, set_log_level, set_log_file
43

54
__version__ = "0.5.dev"
65

surfer/io.py

Lines changed: 0 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import os
2-
from os.path import join as pjoin
32
from tempfile import mktemp
43

54
from subprocess import Popen, PIPE
@@ -11,9 +10,6 @@
1110
import logging
1211
logger = logging.getLogger('surfer')
1312

14-
from .config import config
15-
from .utils import _get_subjects_dir
16-
1713

1814
def read_scalar_data(filepath):
1915
"""Load in scalar data from an image.
@@ -243,115 +239,3 @@ def project_volume_data(filepath, hemi, reg_file=None, subject_id=None,
243239
surf_data = read_scalar_data(out_file)
244240
os.remove(out_file)
245241
return surf_data
246-
247-
248-
class Surface(object):
249-
"""Container for surface object
250-
251-
Attributes
252-
----------
253-
subject_id : string
254-
Name of subject
255-
hemi : {'lh', 'rh'}
256-
Which hemisphere to load
257-
surf : string
258-
Name of the surface to load (eg. inflated, orig ...)
259-
data_path : string
260-
Path where to look for data
261-
x: 1d array
262-
x coordinates of vertices
263-
y: 1d array
264-
y coordinates of vertices
265-
z: 1d array
266-
z coordinates of vertices
267-
coords : 2d array of shape [n_vertices, 3]
268-
The vertices coordinates
269-
faces : 2d array
270-
The faces ie. the triangles
271-
subjects_dir : str | None
272-
If not None, this directory will be used as the subjects directory
273-
instead of the value set using the SUBJECTS_DIR environment variable.
274-
"""
275-
276-
def __init__(self, subject_id, hemi, surf, subjects_dir=None,
277-
offset=None):
278-
"""Surface
279-
280-
Parameters
281-
----------
282-
subject_id : string
283-
Name of subject
284-
hemi : {'lh', 'rh'}
285-
Which hemisphere to load
286-
surf : string
287-
Name of the surface to load (eg. inflated, orig ...)
288-
offset : float | None
289-
If 0.0, the surface will be offset such that the medial
290-
wall is aligned with the origin. If None, no offset will
291-
be applied. If != 0.0, an additional offset will be used.
292-
"""
293-
if hemi not in ['lh', 'rh']:
294-
raise ValueError('hemi must be "lh" or "rh')
295-
self.subject_id = subject_id
296-
self.hemi = hemi
297-
self.surf = surf
298-
self.offset = offset
299-
300-
subjects_dir = _get_subjects_dir(subjects_dir)
301-
self.data_path = pjoin(subjects_dir, subject_id)
302-
303-
def load_geometry(self):
304-
surf_path = pjoin(self.data_path, "surf",
305-
"%s.%s" % (self.hemi, self.surf))
306-
self.coords, self.faces = nib.freesurfer.read_geometry(surf_path)
307-
if self.offset is not None:
308-
if self.hemi == 'lh':
309-
self.coords[:, 0] -= (np.max(self.coords[:, 0]) + self.offset)
310-
else:
311-
self.coords[:, 0] -= (np.min(self.coords[:, 0]) + self.offset)
312-
313-
def save_geometry(self):
314-
surf_path = pjoin(self.data_path, "surf",
315-
"%s.%s" % (self.hemi, self.surf))
316-
nib.freesurfer.write_geometry(surf_path, self.coords, self.faces)
317-
318-
@property
319-
def x(self):
320-
return self.coords[:, 0]
321-
322-
@property
323-
def y(self):
324-
return self.coords[:, 1]
325-
326-
@property
327-
def z(self):
328-
return self.coords[:, 2]
329-
330-
def load_curvature(self):
331-
"""Load in curvature values from the ?h.curv file."""
332-
curv_path = pjoin(self.data_path, "surf", "%s.curv" % self.hemi)
333-
self.curv = nib.freesurfer.read_morph_data(curv_path)
334-
self.bin_curv = np.array(self.curv > 0, np.int)
335-
336-
def load_label(self, name):
337-
"""Load in a Freesurfer .label file.
338-
339-
Label files are just text files indicating the vertices included
340-
in the label. Each Surface instance has a dictionary of labels, keyed
341-
by the name (which is taken from the file name if not given as an
342-
argument.
343-
344-
"""
345-
label = nib.freesurfer.read_label(pjoin(self.data_path, 'label',
346-
'%s.%s.label' % (self.hemi, name)))
347-
label_array = np.zeros(len(self.x), np.int)
348-
label_array[label] = 1
349-
try:
350-
self.labels[name] = label_array
351-
except AttributeError:
352-
self.labels = {name: label_array}
353-
354-
def apply_xfm(self, mtx):
355-
"""Apply an affine transformation matrix to the x,y,z vectors."""
356-
self.coords = np.dot(np.c_[self.coords, np.ones(len(self.coords))],
357-
mtx.T)[:, :3]

surfer/tests/test_io.py renamed to surfer/tests/test_utils.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@
33
import numpy as np
44
from numpy.testing import assert_array_almost_equal
55

6-
from surfer import io
7-
from surfer.utils import requires_fsaverage
6+
from surfer import utils
87

9-
subj_dir = io._get_subjects_dir()
8+
subj_dir = utils._get_subjects_dir()
109
subject_id = 'fsaverage'
1110
data_path = pjoin(subj_dir, subject_id)
1211

1312

14-
@requires_fsaverage
13+
@utils.requires_fsaverage
1514
def test_surface():
1615
"""Test IO for Surface class"""
1716
for subjects_dir in [None, subj_dir]:
18-
surface = io.Surface('fsaverage', 'lh', 'inflated',
17+
surface = utils.Surface('fsaverage', 'lh', 'inflated',
1918
subjects_dir=subjects_dir)
2019
surface.load_geometry()
2120
surface.load_label('BA1')

surfer/tests/test_viz.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
import nibabel as nib
77

88
from surfer import Brain
9-
from surfer import io
9+
from surfer import io, utils
1010
from surfer.utils import requires_fsaverage
1111
from mayavi import mlab
1212

13-
subj_dir = io._get_subjects_dir()
13+
subj_dir = utils._get_subjects_dir()
1414
subject_id = 'fsaverage'
1515
std_args = [subject_id, 'lh', 'inflated']
1616
data_dir = pjoin(os.path.split(__file__)[0], '..', '..',

surfer/utils.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,127 @@
77
from functools import wraps
88

99
import numpy as np
10+
import nibabel as nib
1011
from scipy import sparse
1112
from scipy.spatial.distance import cdist
13+
1214
from .config import config
1315

1416
logger = logging.getLogger('surfer')
1517

1618

19+
class Surface(object):
20+
"""Container for surface object
21+
22+
Attributes
23+
----------
24+
subject_id : string
25+
Name of subject
26+
hemi : {'lh', 'rh'}
27+
Which hemisphere to load
28+
surf : string
29+
Name of the surface to load (eg. inflated, orig ...)
30+
data_path : string
31+
Path where to look for data
32+
x: 1d array
33+
x coordinates of vertices
34+
y: 1d array
35+
y coordinates of vertices
36+
z: 1d array
37+
z coordinates of vertices
38+
coords : 2d array of shape [n_vertices, 3]
39+
The vertices coordinates
40+
faces : 2d array
41+
The faces ie. the triangles
42+
subjects_dir : str | None
43+
If not None, this directory will be used as the subjects directory
44+
instead of the value set using the SUBJECTS_DIR environment variable.
45+
"""
46+
47+
def __init__(self, subject_id, hemi, surf, subjects_dir=None,
48+
offset=None):
49+
"""Surface
50+
51+
Parameters
52+
----------
53+
subject_id : string
54+
Name of subject
55+
hemi : {'lh', 'rh'}
56+
Which hemisphere to load
57+
surf : string
58+
Name of the surface to load (eg. inflated, orig ...)
59+
offset : float | None
60+
If 0.0, the surface will be offset such that the medial
61+
wall is aligned with the origin. If None, no offset will
62+
be applied. If != 0.0, an additional offset will be used.
63+
"""
64+
if hemi not in ['lh', 'rh']:
65+
raise ValueError('hemi must be "lh" or "rh')
66+
self.subject_id = subject_id
67+
self.hemi = hemi
68+
self.surf = surf
69+
self.offset = offset
70+
71+
subjects_dir = _get_subjects_dir(subjects_dir)
72+
self.data_path = op.join(subjects_dir, subject_id)
73+
74+
def load_geometry(self):
75+
surf_path = op.join(self.data_path, "surf",
76+
"%s.%s" % (self.hemi, self.surf))
77+
self.coords, self.faces = nib.freesurfer.read_geometry(surf_path)
78+
if self.offset is not None:
79+
if self.hemi == 'lh':
80+
self.coords[:, 0] -= (np.max(self.coords[:, 0]) + self.offset)
81+
else:
82+
self.coords[:, 0] -= (np.min(self.coords[:, 0]) + self.offset)
83+
84+
def save_geometry(self):
85+
surf_path = op.join(self.data_path, "surf",
86+
"%s.%s" % (self.hemi, self.surf))
87+
nib.freesurfer.write_geometry(surf_path, self.coords, self.faces)
88+
89+
@property
90+
def x(self):
91+
return self.coords[:, 0]
92+
93+
@property
94+
def y(self):
95+
return self.coords[:, 1]
96+
97+
@property
98+
def z(self):
99+
return self.coords[:, 2]
100+
101+
def load_curvature(self):
102+
"""Load in curvature values from the ?h.curv file."""
103+
curv_path = op.join(self.data_path, "surf", "%s.curv" % self.hemi)
104+
self.curv = nib.freesurfer.read_morph_data(curv_path)
105+
self.bin_curv = np.array(self.curv > 0, np.int)
106+
107+
def load_label(self, name):
108+
"""Load in a Freesurfer .label file.
109+
110+
Label files are just text files indicating the vertices included
111+
in the label. Each Surface instance has a dictionary of labels, keyed
112+
by the name (which is taken from the file name if not given as an
113+
argument.
114+
115+
"""
116+
label = nib.freesurfer.read_label(op.join(self.data_path, 'label',
117+
'%s.%s.label' % (self.hemi, name)))
118+
label_array = np.zeros(len(self.x), np.int)
119+
label_array[label] = 1
120+
try:
121+
self.labels[name] = label_array
122+
except AttributeError:
123+
self.labels = {name: label_array}
124+
125+
def apply_xfm(self, mtx):
126+
"""Apply an affine transformation matrix to the x,y,z vectors."""
127+
self.coords = np.dot(np.c_[self.coords, np.ones(len(self.coords))],
128+
mtx.T)[:, :3]
129+
130+
17131
###############################################################################
18132
# LOGGING (courtesy of mne-python)
19133

surfer/viz.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@
1313
from mayavi.core.ui.api import SceneEditor
1414
from mayavi.core.ui.mayavi_scene import MayaviScene
1515

16-
from . import io
17-
from . import utils
18-
from .io import Surface, _get_subjects_dir
16+
from . import utils, io
1917
from .config import config
20-
from .utils import verbose
18+
from .utils import Surface, verbose, _get_subjects_dir
19+
2120

2221
import logging
2322
logging.basicConfig() # suppress "No handlers found for logger" error
@@ -1233,8 +1232,8 @@ def add_foci(self, coords, coords_as_verts=False, map_surface=None,
12331232
if map_surface is None:
12341233
foci_coords = np.atleast_2d(coords)
12351234
else:
1236-
foci_surf = io.Surface(self.subject_id, hemi, map_surface,
1237-
subjects_dir=self.subjects_dir)
1235+
foci_surf = Surface(self.subject_id, hemi, map_surface,
1236+
subjects_dir=self.subjects_dir)
12381237
foci_surf.load_geometry()
12391238
foci_vtxs = utils.find_closest_vertices(foci_surf.coords, coords)
12401239
foci_coords = self.geo[hemi].coords[foci_vtxs]

0 commit comments

Comments
 (0)