Skip to content

Commit cacaa1f

Browse files
committed
flexible vox2surfRAS conversion in write_fssurf
1 parent 8868d3c commit cacaa1f

2 files changed

Lines changed: 56 additions & 14 deletions

File tree

lapy/_tria_io.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,12 @@ def write_vtk(tria: "TriaMesh", filename: str) -> None:
534534
f.close()
535535

536536

537-
def write_fssurf(tria: "TriaMesh", filename: str, image: Optional[object] = None) -> None:
537+
def write_fssurf(
538+
tria: "TriaMesh",
539+
filename: str,
540+
image: Optional[object] = None,
541+
coords_are_voxels: Optional[bool] = None,
542+
) -> None:
538543
"""Save Freesurfer Surface Geometry file (wrap Nibabel).
539544
540545
Parameters
@@ -544,13 +549,23 @@ def write_fssurf(tria: "TriaMesh", filename: str, image: Optional[object] = None
544549
filename : str
545550
Filename to save to.
546551
image : str, object, or None, default=None
547-
Path to image, nibabel image object, or image header. If specified, the vertices
548-
are assumed to be in voxel coordinates and are converted to surface RAS (tkr)
549-
coordinates before saving. The expected order of coordinates is (x, y, z)
550-
matching the image voxel indices in nibabel.
552+
Path to image, nibabel image object, or image header. If specified, volume_info
553+
will be extracted from the image header, and by default, vertices are assumed to
554+
be in voxel coordinates and will be converted to surface RAS (tkr) before saving.
555+
The expected order of coordinates is (x, y, z) matching the image voxel indices
556+
in nibabel.
557+
coords_are_voxels : bool or None, default=None
558+
Specifies whether vertices are in voxel coordinates. If None (default), the
559+
behavior is inferred: when image is provided, vertices are assumed to be in
560+
voxel space and converted to surface RAS; when image is not provided, vertices
561+
are assumed to already be in surface RAS. Set explicitly to True to force
562+
conversion (requires image), or False to skip conversion even when image is
563+
provided (only extracts volume_info).
551564
552565
Raises
553566
------
567+
ValueError
568+
If coords_are_voxels is explicitly True but image is None.
554569
OSError
555570
If file is not writable.
556571
TypeError
@@ -562,6 +577,17 @@ def write_fssurf(tria: "TriaMesh", filename: str, image: Optional[object] = None
562577
``get_vox2ras_tkr()`` (e.g., ``MGHHeader``). For other header types (NIfTI1/2,
563578
Analyze/SPM, etc.), we attempt conversion via ``MGHHeader.from_header``.
564579
"""
580+
# Infer coords_are_voxels if not explicitly set
581+
if coords_are_voxels is None:
582+
coords_are_voxels = image is not None
583+
584+
# Validate parameters
585+
if coords_are_voxels and image is None:
586+
raise ValueError(
587+
"coords_are_voxels=True requires an image to be provided for voxel-to-surface-RAS "
588+
"coordinate conversion. Either provide an image or set coords_are_voxels=False."
589+
)
590+
565591
# open file
566592
try:
567593
from nibabel.freesurfer.io import write_geometry
@@ -594,7 +620,9 @@ def write_fssurf(tria: "TriaMesh", filename: str, image: Optional[object] = None
594620
"via MGHHeader.from_header)."
595621
) from e
596622

597-
v = apply_affine(header.get_vox2ras_tkr(), v)
623+
# Convert from voxel to surface RAS coordinates if requested
624+
if coords_are_voxels:
625+
v = apply_affine(header.get_vox2ras_tkr(), v)
598626

599627
# create volume_info from header
600628
affine = header.get_best_affine()

lapy/tria_mesh.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -201,33 +201,47 @@ def write_vtk(self, filename: str) -> None:
201201
"""
202202
io.write_vtk(self, filename)
203203

204-
def write_fssurf(self, filename: str, image: Optional[object] = None) -> None:
204+
def write_fssurf(
205+
self,
206+
filename: str,
207+
image: Optional[object] = None,
208+
coords_are_voxels: Optional[bool] = None,
209+
) -> None:
205210
"""Save as Freesurfer Surface Geometry file (wrap Nibabel).
206211
207212
Parameters
208213
----------
209214
filename : str
210215
Filename to save to.
211216
image : str, object, None
212-
Path to image, nibabel image object, or image header. If specified, the vertices
213-
are assumed to be in voxel coordinates and are converted to surface RAS (tkr)
214-
coordinates before saving. The expected order of coordinates is (x, y, z)
215-
matching the image voxel indices in nibabel.
217+
Path to image, nibabel image object, or image header. If specified, volume_info
218+
will be extracted from the image header, and by default, vertices are assumed to
219+
be in voxel coordinates and will be converted to surface RAS (tkr) before saving.
220+
The expected order of coordinates is (x, y, z) matching the image voxel indices
221+
in nibabel.
222+
coords_are_voxels : bool or None, default=None
223+
Specifies whether vertices are in voxel coordinates. If None (default), the
224+
behavior is inferred: when image is provided, vertices are assumed to be in
225+
voxel space and converted to surface RAS; when image is not provided, vertices
226+
are assumed to already be in surface RAS. Set explicitly to True to force
227+
conversion (requires image), or False to skip conversion even when image is
228+
provided (only extracts volume_info).
216229
217230
Raises
218231
------
219-
IOError
220-
If file cannot be written.
221232
ValueError
233+
If coords_are_voxels is explicitly True but image is None.
222234
If image header cannot be processed.
235+
IOError
236+
If file cannot be written.
223237
224238
Notes
225239
-----
226240
The surface RAS (tkr) transform is obtained from a header that implements
227241
``get_vox2ras_tkr()`` (e.g., ``MGHHeader``). For other header types (NIfTI1/2,
228242
Analyze/SPM, etc.), we attempt conversion via ``MGHHeader.from_header``.
229243
"""
230-
io.write_fssurf(self, filename, image=image)
244+
io.write_fssurf(self, filename, image=image, coords_are_voxels=coords_are_voxels)
231245

232246
def _construct_adj_sym(self):
233247
"""Construct symmetric adjacency matrix (edge graph) of triangle mesh.

0 commit comments

Comments
 (0)