The goal of ieegio is to provide integrated toolbox for common file formats used in intracranial Electroencephalography (iEEG) and deep-brain stimulation (DBS). Besides reading and writing files, ieegio also maps data between volumes and surfaces, chains coordinate transforms, and compares regions of interest. Currently the package is under active development.
Installation
Install the released version from CRAN:
install.packages("ieegio")Or the development version from r-universe:
install.packages("ieegio", repos = "https://dipterix.r-universe.dev")Or from GitHub:
# install.packages("pak")
pak::pak("dipterix/ieegio")ieegio provides additional sample data. To download them:
# list all the sample data
sample_names <- ieegio::ieegio_sample_data()
lapply(sample_names, ieegio::ieegio_sample_data)All examples below use these sample files.
Electrophysiology
Read a recording, then pull a channel out by number or by label:
library(ieegio)
edf <- read_edf(ieegio_sample_data("edfPlusD.edf"))
# channel metadata as a data frame
head(edf$get_channel_table())
# by number, or by label
channel <- edf$get_channel(1)
channel <- edf$get_channel("squarewave")To write a recording, build the channels with as_edf_channel. A channel is either a signal or an annotation table:
signal <- sin(seq(0, 10, by = 0.01))
channels <- list(
as_edf_channel(signal, channel_num = 1, sample_rate = 200, label = "sine"),
as_edf_channel(
data.frame(timestamp = c(0, 5), comments = c("start", "end")),
channel_num = 2
)
)
path <- tempfile(fileext = ".edf")
write_edf(channels = channels, con = path)See the ephys article for more.
Volumes and surfaces
read_volume and read_surface are the universal entry points; the format is inferred from the file name. Volumes carry a header, the data, and a transforms list mapping voxel index to other coordinate systems.
volume <- read_volume(ieegio_sample_data("brain.demosubject.nii.gz"))
# index with `[`, plot anatomical slices
volume[128, , ]
plot(volume, position = c(-50, -10, 15), which = "coronal")Surface files each hold one kind of data, and merge combines them into a single object:
geometry <- read_surface(ieegio_sample_data("gifti/icosahedron3d/geometry.gii"))
measurement <- read_surface(ieegio_sample_data("gifti/icosahedron3d/rand.gii"))
merged <- merge(geometry, measurement)
plot(merged, name = list("measurements", 1))See the imaging article for more.
Surface annotations from AFNI/SUMA
NIML datasets (*.niml.dset) are read by read_surface, which decides between labels and continuous values from the dataset itself. Sample data includes matching std.141 geometry and annotations:
geometry <- read_surface(
ieegio_sample_data("gifti/std.141.lh.inf_200.gii"))
annotation <- read_surface(
ieegio_sample_data("niml/std.141.lh.aparc.a2009s.annot.niml.dset"))
merge(geometry, annotation)
#> <ieegio Surface>
#> Header class: basic_geometry
#> Geometry :
#> # of Vertex : 198812
#> # of Face index : 397620
#> Annotations: `node label`
#> # of labels: 76
#>
#> Contains: `geometry`, `annotations`io_read_niml is the low-level reader if you need the raw element tree. All NIML storage forms are supported (plain text, binary and base64 in either byte order, optionally gzip compressed).
Color maps
read_colormap handles FreeSurfer lookup tables, .annot color tables, AFNI label tables, FSL color maps, and threeBrain/RAVE JSON:
cmap <- read_colormap(
ieegio_sample_data("niml/rh.std.141.Glasser_HCP.lbl.niml.dset"))
cmap
#> <ieegio Colormap [discrete]>
#> Colorspace : RGB
#> Color stops : 181 (keys 0 to 180)
#> Lookup : 181 labels
# write as a FreeSurfer lookup table
write_colormap(cmap, tempfile(fileext = ".txt"), format = "fs_lut")Regions of interest
A region of interest is described first and computed later. as_ieegio_roi records the criteria, resolve_roi_as carries them out and hands back geometry in world (RAS) coordinates, and detect_roi_overlap compares two regions.
atlas <- read_volume(ieegio_sample_data("atlases/YBA/YBA690.nii.gz"))
# describe: which voxels count as the region
roi <- as_ieegio_roi(atlas, threshold_lb = 1, threshold_ub = 5)
# compute: turn the description into geometry
resolve_roi_as(roi, "pointcloud")
#> <ieegio ROI [pointcloud]>
#> ... # of Vertex : 3495Because both sides are resolved to world coordinates first, regions of different kinds can be compared directly. Here the whole atlas is tested against a facial-nerve tract bundle:
tracts <- read_streamlines(
ieegio_sample_data("streamlines/CNVII_R.trk"), half_voxel_offset = TRUE)
detect_roi_overlap(as_ieegio_roi(atlas, threshold_lb = 1), tracts, radius = 2)
#> <ieegio ROI overlap: `x` and `y` overlap>
#> x: volume
#> y: streamlines (annotated)
#> Units of `y` overlapping `x`: 53.7%
#> Early stop: FALSEThe result also carries the annotated tracts back, so each streamline knows whether it hit the region.
Coordinate spaces and transforms
Name a space with new_space, then move a surface into it. Transforms can be read from ANTs or FSL FLIRT files, or chained with as_ieegio_transform.
mni <- new_space("MNI152", orientation = "RAS")
surface <- read_surface(ieegio_sample_data("gifti/icosahedron3d/geometry.gii"))
surface_to_surface(surface, space_from = "scanner", space_to = mni,
transform = diag(1, 4))
#> ...
#> # of transforms : 1
#> Transform Targets : MNI152| Purpose | Function |
|---|---|
| Define a coordinate space | new_space |
| Build or chain transforms | as_ieegio_transform |
Read ANTs affine / displacement field |
io_read_ants_transform |
Read FSL FLIRT matrix |
io_read_flirt_transform |
Convert FLIRT to world (RAS) |
transform_flirt2ras |
| Transform between orientations | transform_orientation |
| Move a surface between spaces | surface_to_surface |
Mapping and editing volumes
# volume mask or atlas label to a smoothed mesh
volume_to_surface(atlas, threshold_lb = 1, threshold_ub = 5)
# down-sample or super-sample
resample_volume(volume, new_dim = c(128, 128, 128))
# burn electrode contacts at RAS positions
burn_volume(volume, ras_position = rbind(c(-50, -10, 15), c(-40, -10, 15)),
col = c("red", "green"), radius = 2)burn_curve does the same for a trajectory, such as a depth (sEEG) lead or a DBS shaft, given start and end positions.
Supported formats
The following formats have been (or will be) supported. Please feel free to make a wish-list by posting an issue in this repository.
Electrophysiology (examples)
| Name | Common Extensions | Read | Write |
|---|---|---|---|
| EDF(+)/BDF(+) (European Data Format) | *.edf |
read_edf |
write_edf |
| BrainVision |
*.vhdr, *.vmrk, *.eeg, *.dat
|
read_brainvis |
|
| BCI2000 | *.dat |
read_bci2000 |
|
| NEV/NSx (BlackRock neural-event/signal) |
*.nev, *.ns1, *.ns2, …, *.ns6
|
read_nsx |
|
| NWB (Neurodata Without Borders) | *.nwb |
read_nwb |
* |
| MEF | *.mef |
* - NWB format only has low-level support due to its format nature. Please see examples with help(read_nwb)
Imaging (examples)
| Name | Common Extensions | Read | Write |
|---|---|---|---|
| NIfTI (Neuroimaging Informatics Technology Initiative) | *.nii[.gz] |
read_volume (io_read_nii) |
write_volume (io_write_nii) |
| MGH/MGZ (Massachusetts General Hospital format) |
*.mgh, *.mgz
|
read_volume (io_read_mgz) |
write_volume (io_write_mgz) |
| GIfTI (Geometry format under the NIfTI) | *.gii[.gz] |
read_surface (io_read_gii) |
write_surface (io_write_gii) |
| FreeSurfer surface | e.g. *h.pial, *h.white
|
read_surface (io_read_fs(type="geometry")) |
write_surface(format="freesurfer", type="geometry") |
| FreeSurfer node values/weights | e.g. *h.curv, *h.sulc
|
read_surface (io_read_fs(type="measurements")) |
write_surface(format="freesurfer", type="measurements") |
| Surface annotation | *.annot |
read_surface (io_read_fs(type="annotations")) |
write_surface(format="freesurfer", type="annotations") |
AFNI/SUMA NIML surface data |
*.niml.dset |
read_surface (io_read_niml) |
|
VTK poly-data (as surface mesh) |
*.vtk, *.vtp, *.pvtp, *.vtu, *.vtpb
|
read_surface (io_read_vtk_polys) |
write_surface (io_write_vtk_polys) ** |
| Other mesh formats |
*.stl, *.ply, *.obj, *.off, *.mz3
|
read_surface (via freesurferformats) |
write_surface (*.stl only) |
TRK streamlines |
*.trk, *.trk.gz
|
read_streamlines (io_read_trk) |
write_streamlines (io_write_trk) |
TCK streamlines |
*.tck |
read_streamlines (io_read_tck) |
write_streamlines (io_write_tck) |
TT (DSI-Studio) streamlines |
*.tt, *.tt.gz
|
read_streamlines (io_read_tt) |
|
VTK poly-data (as streamlines) |
*.vtk, *.vtp
|
read_streamlines (io_read_vtk_streamlines) *** |
write_streamlines (io_write_vtk_streamlines) |
** - VTK surfaces can be read from all five extensions, but only written as legacy *.vtk, XML *.vtp, or HDF5 *.vtpb.
*** - read_streamlines infers *.vtk and *.vtp from the file name; call io_read_vtk_streamlines directly for *.pvtp and *.vtpb. write_streamlines also writes *.vtpb and *.h5.
The VTK readers and writers use the Python vtk package.
Color maps
| Name | Common Extensions | Read | Write |
|---|---|---|---|
FreeSurfer lookup table |
*.txt |
read_colormap |
write_colormap(format="fs_lut") |
FreeSurfer annotation |
*.annot |
read_colormap |
|
AFNI label table |
*.niml.dset, *.lut
|
read_colormap |
|
FSL color map / VEST
|
*.cmap, *.lut
|
read_colormap |
|
threeBrain/RAVE
|
*.json |
read_colormap |
write_colormap (default) |
Meta data
| Name | Common Extensions | Read | Write |
|---|---|---|---|
| Matlab | *.mat |
io_read_mat |
io_write_mat |
| HDF5 | *.h5 |
io_read_h5 |
io_write_h5 |
| YAML |
*.yml, *.yaml
|
io_read_yaml |
io_write_yaml |
| JSON | *.json |
io_read_json |
io_write_json |
| INI | *.ini |
io_read_ini |
|
| FST | *.fst |
io_read_fst |
io_write_fst |
The HDF5 back-end is resolved at run time, choosing among readNSx, h5lite, hdf5r, and h5py, so no single one of them is required. LazyH5 and LazyFST read HDF5 and FST files lazily, and convert_fst_to_csv and convert_fst_to_hdf5 convert between the formats.