Files
rustytorch/crates/specialized/rtx-neuro-io/src/lib.rs
T
2026-03-04 00:08:42 +00:00

220 lines
6.1 KiB
Rust

//! # rtx-neuro-io
//!
//! File format readers and writers for neuroimaging data.
//!
//! ## Supported Formats
//!
//! ### EEG Formats
//! - **EDF/EDF+** - European Data Format (most common research format)
//! - **BDF** - BioSemi 24-bit variant
//! - **BrainVision** - .vhdr/.vmrk/.eeg files
//! - **EGI** - Electrical Geodesics (.raw, .mff)
//!
//! ### MEG Formats
//! - **FIF** - Elekta/Neuromag format
//! - **CTF** - CTF MEG Systems (.ds directories)
//! - **BTi/4D** - 4D-Neuroimaging/BTi systems
//! - **KIT** - Yokogawa/KIT/Ricoh systems (.con, .sqd)
//!
//! ### Dataset Formats
//! - **BIDS** - Brain Imaging Data Structure (directory-based)
//!
//! ## Usage
//!
//! ```rust,ignore
//! use rtx_neuro_io::edf::EdfReader;
//! use rtx_neuro_io::fif::FifReader;
//! use rtx_neuro_io::ctf::CtfReader;
//! use rtx_neuro_io::bti::BtiReader;
//! use rtx_neuro_io::kit::KitReader;
//! use rtx_neuro_io::egi::EgiReader;
//!
//! // Read EDF file
//! let reader = EdfReader::open("recording.edf")?;
//! let data = reader.read_data(0.0, 10.0)?; // Read 10 seconds
//!
//! // Read FIF file (Elekta/Neuromag MEG)
//! let reader = FifReader::open("sample_raw.fif")?;
//! let data = reader.read_data(0.0, 10.0)?;
//!
//! // Read CTF dataset (directory-based)
//! let reader = CtfReader::open("experiment.ds")?;
//! let data = reader.read_data(0.0, 10.0)?;
//!
//! // Read 4D-Neuroimaging/BTi data
//! let reader = BtiReader::open("subject_data")?;
//! let data = reader.read_data(0.0, 10.0)?;
//!
//! // Read Yokogawa/KIT data
//! let reader = KitReader::open("recording.con")?;
//! let data = reader.read_data(0.0, 10.0)?;
//!
//! // Read EGI data
//! let reader = EgiReader::open("recording.raw")?;
//! let data = reader.read_data(0.0, 10.0)?;
//!
//! // Open BIDS dataset
//! use rtx_neuro_io::bids::BidsDataset;
//! let dataset = BidsDataset::open("my_bids_dataset")?;
//! println!("Dataset: {}", dataset.name());
//! for subject in dataset.subject_labels() {
//! println!(" Subject: {}", subject);
//! }
//! ```
#![warn(missing_docs)]
pub mod bids;
pub mod brainvision;
pub mod bti;
pub mod ctf;
pub mod edf;
pub mod egi;
pub mod fif;
pub mod kit;
// Re-export main types
pub use bids::{
BidsDataset, BidsFile, BidsSession, BidsSubject, DatasetDescription, FileEntities,
is_bids_dataset, parse_bids_filename,
};
pub use brainvision::BrainVisionReader;
pub use bti::{BtiChannel, BtiChannelKind, BtiConfig, BtiReader};
pub use ctf::{CtfChannel, CtfChannelKind, CtfReader, Res4Header};
pub use edf::{EdfHeader, EdfReader};
pub use egi::{EgiChannel, EgiChannelKind, EgiFormat, EgiHeader, EgiReader};
pub use fif::{FifChannel, FifInfo, FifReader};
pub use kit::{KitChannel, KitChannelKind, KitHeader, KitReader};
use std::path::Path;
/// Error types for I/O operations
#[derive(Debug, thiserror::Error)]
pub enum IoError {
/// File I/O error
#[error("File I/O error: {0}")]
Io(#[from] std::io::Error),
/// Invalid file format
#[error("Invalid file format: {0}")]
InvalidFormat(String),
/// Unsupported format version
#[error("Unsupported format version: {0}")]
UnsupportedVersion(String),
/// Header parsing error
#[error("Header parsing error: {0}")]
HeaderParse(String),
/// Data parsing error
#[error("Data parsing error: {0}")]
DataParse(String),
/// File not found
#[error("File not found: {0}")]
FileNotFound(String),
/// Channel not found
#[error("Channel not found: {0}")]
ChannelNotFound(String),
}
/// Result type for I/O operations
pub type IoResult<T> = Result<T, IoError>;
/// Trait for file format readers
pub trait NeuroReader {
/// Read file header/metadata
fn read_header(&mut self) -> IoResult<()>;
/// Get sampling frequency in Hz
fn sfreq(&self) -> f64;
/// Get number of channels
fn n_channels(&self) -> usize;
/// Get total number of samples per channel
fn n_samples(&self) -> usize;
/// Get channel names
fn channel_names(&self) -> Vec<String>;
/// Read raw data for specified time range
/// Returns data as [n_channels x n_samples]
fn read_raw_data(&mut self, tmin: f64, tmax: f64) -> IoResult<Vec<f64>>;
/// Read all data
fn read_all_data(&mut self) -> IoResult<Vec<f64>> {
let duration = self.n_samples() as f64 / self.sfreq();
self.read_raw_data(0.0, duration)
}
}
/// Detect file format from path
pub fn detect_format(path: impl AsRef<Path>) -> Option<FileFormat> {
let path = path.as_ref();
// Check for directory-based formats
if path.is_dir() {
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
if name.ends_with(".ds") {
return Some(FileFormat::Ctf);
}
if name.ends_with(".mff") {
return Some(FileFormat::Egi);
}
// Check for BTi directory (contains config file)
let config_path = path.join("config");
if config_path.exists() {
return Some(FileFormat::Bti);
}
}
}
// Check file extension
let ext = path.extension()?.to_str()?.to_lowercase();
match ext.as_str() {
"edf" => Some(FileFormat::Edf),
"bdf" => Some(FileFormat::Bdf),
"vhdr" => Some(FileFormat::BrainVision),
"set" => Some(FileFormat::EegLab),
"fif" => Some(FileFormat::Fif),
"ds" => Some(FileFormat::Ctf),
"cnt" => Some(FileFormat::Neuroscan),
"nwb" => Some(FileFormat::Nwb),
"con" | "sqd" => Some(FileFormat::Kit),
"raw" => Some(FileFormat::Egi),
"mff" => Some(FileFormat::Egi),
_ => None,
}
}
/// Supported file formats
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FileFormat {
/// EDF (European Data Format)
Edf,
/// BDF (BioSemi 24-bit)
Bdf,
/// BrainVision (.vhdr/.vmrk/.eeg)
BrainVision,
/// EEGLAB (.set)
EegLab,
/// Elekta/Neuromag FIF
Fif,
/// CTF MEG
Ctf,
/// 4D-Neuroimaging/BTi MEG
Bti,
/// Yokogawa/KIT/Ricoh MEG
Kit,
/// EGI (.raw, .mff)
Egi,
/// Neuroscan (.cnt)
Neuroscan,
/// Neurodata Without Borders
Nwb,
}