Complete workflows for preprocessing EEG data with external tools and using the features in Nimbus BCI (Python and Julia SDKs).These pipelines work with any Nimbus model once you have valid features:
Python SDK Users: The nimbus-bci package has native MNE-Python integration! See the MNE Integration guide for streamlined workflows.Julia SDK Users: This guide shows how to preprocess in external tools and load into Julia.
New to preprocessing? Start with Preprocessing Requirements for an overview before diving into specific tools.
For Julia SDK users: This section shows how to preprocess in Python with MNE and export to Julia.Python SDK users: Use the native MNE integration instead (see above).
Complete pipeline for Motor Imagery preprocessing with MNE-Python and export to Julia SDK.
import pandas as pdimport numpy as np# OpenVibe exports as CSVdf = pd.read_csv('openvibe_output.csv')# Assuming format: Time, Channel1, Channel2, ..., Labelfeatures_raw = df.iloc[:, 1:-1].values.T # Transpose to (channels × samples)labels = df['Label'].values# Reshape for NimbusSDK (if you have trial boundaries)# You'll need to segment based on labelsfeatures_segmented = segment_by_labels(features_raw, labels)# Result: (n_features, n_samples_per_trial, n_trials)# Save for Juliaimport scipy.io as siosio.savemat('openvibe_features.mat', { 'features': features_segmented, 'labels': labels})
# CSV: Time series with labelsdf = pd.read_csv('data.csv')# Convert to trials (segment by label changes)features_julia = segment_to_trials(df) # (n_features, n_samples, n_trials)
def check_preprocessing_quality(features, labels): """Check data quality before saving for Julia""" # Check shape assert features.ndim == 3, "Features must be 3D: (features, samples, trials)" n_features, n_samples, n_trials = features.shape # Check for NaN/Inf assert not np.any(np.isnan(features)), "Features contain NaN values" assert not np.any(np.isinf(features)), "Features contain Inf values" # Check labels assert len(labels) == n_trials, "Labels must match number of trials" assert labels.min() >= 1, "Labels must be 1-indexed (start at 1)" assert labels.max() <= len(np.unique(labels)), "Labels must be consecutive" # Check value ranges (not too large) assert np.abs(features).max() < 1e6, "Features have suspiciously large values" print("✓ All quality checks passed!") print(f" Shape: ({n_features}, {n_samples}, {n_trials})") print(f" Classes: {np.unique(labels)}") print(f" Range: [{features.min():.3f}, {features.max():.3f}]") return True# Use itcheck_preprocessing_quality(X_csp_julia, y)
Critical for cross-session BCI performance!EEG amplitude varies 50-200% across sessions. Always normalize your features for models used across sessions.
using NimbusSDK# 1. Estimate normalization parameters from TRAINING datatrain_features = csp_features_train # (16 × 250 × 80)norm_params = estimate_normalization_params(train_features; method=:zscore)# 2. Apply to BOTH training and test datatrain_norm = apply_normalization(train_features, norm_params)test_norm = apply_normalization(test_features, norm_params)# 3. Save parameters with your model@save "model_with_norm.jld2" model norm_params# 4. Later: Load and apply same parameters@load "model_with_norm.jld2" model norm_paramsnew_data_norm = apply_normalization(new_data, norm_params)