Signal Processing and Feature Extraction
This tutorial explains the main EMG signal processing utilities in intan.processing
, including filtering, rectification, normalization, feature extraction, and dimensionality reduction.
All functions are compatible with multi-channel EMG arrays loaded from .rhd or .dat files.
Filtering and Preprocessing
The intan.processing._filters module provides robust EMG filtering routines, including bandpass, lowpass, notch filtering, and rectification.
Example: Bandpass and Notch Filtering
from intan.processing import bandpass_filter, notch_filter, rectify
# Assume emg_data shape is (channels, samples), fs is the sampling rate in Hz
filtered = bandpass_filter(emg_data, lowcut=20, highcut=450, fs=fs)
filtered = notch_filter(filtered, fs=fs, f0=60) # Remove 60 Hz noise
# Rectify EMG (absolute value)
rectified = rectify(filtered)
Windowed and Smoothed RMS
RMS (Root Mean Square) is often used to measure the envelope of EMG signals.
from intan.processing import window_rms, calculate_rms
# Windowed RMS across each channel (e.g., 400-sample window)
windowed_rms = window_rms(emg_data, window_size=400)
# Non-overlapping RMS for feature extraction (returns (channels, windows))
rms_features = calculate_rms(emg_data, window_size=400)
Envelope Extraction
Use the analytic Hilbert envelope for precise EMG envelope estimation:
from intan.processing import envelope_extraction
# Returns same shape as input
envelope = envelope_extraction(emg_data, method='hilbert')
Normalization and Referencing
Z-score normalization: Standardizes each channel to zero mean/unit variance.
from intan.processing import z_score_norm
normalized = z_score_norm(emg_data)
Common Average Referencing (CAR): Remove global artifacts across all channels.
from intan.processing import common_average_reference
car_data = common_average_reference(emg_data)
Dimensionality Reduction (PCA)
Reduce channel count and noise with Principal Component Analysis (PCA):
from intan.processing import apply_pca
# Reduce to 8 principal components
pca_data, explained_var = apply_pca(emg_data, num_components=8)
print("Explained variance ratio:", explained_var)
Sliding Windows for Segmentation
Split continuous EMG into overlapping windows for further analysis:
from intan.processing import sliding_window
# Example: 250 ms windows with 50 ms step size
window_size = int(0.25 * fs)
step_size = int(0.05 * fs)
windows = sliding_window(emg_data, window_size, step_size)
# Each entry is (channels, window_size)
Feature Extraction
The intan.processing._features module provides standard time-domain EMG features:
Mean Absolute Value (MAV)
Zero Crossings (ZC)
Slope Sign Changes (SSC)
Waveform Length (WL)
Root Mean Square (RMS)
Extract all features for each channel in a window:
from intan.processing import extract_features
# Example: compute all features for every channel in a segment
feats = extract_features(emg_window)
print("Feature vector shape:", feats.shape)
You can also specify which features to extract:
feature_list = ['mean_absolute_value', 'zero_crossings', 'waveform_length']
feats = extract_features(emg_window, feature_fns=feature_list)
Full Preprocessing Pipeline Example
The following combines filtering, rectification, and RMS envelope smoothing in a single pipeline:
from intan.processing import bandpass_filter, notch_filter, rectify, window_rms
# Bandpass filter
emg_filtered = bandpass_filter(emg_data, lowcut=20, highcut=450, fs=fs)
# Notch filter for line noise
emg_notched = notch_filter(emg_filtered, fs=fs, f0=60)
# Rectify
emg_rectified = rectify(emg_notched)
# Windowed RMS smoothing
emg_smooth = window_rms(emg_rectified, window_size=400)
Noise Characterization Example
Compare baseline RMS noise between two conditions (e.g., dry vs. wet electrodes):
from intan.processing import bandpass_filter, notch_filter
import numpy as np
# Load EMG for each condition
emg_dry = ... # (channels, samples)
emg_wet = ... # (channels, samples)
fs = 4000
def compute_rms(data):
return np.sqrt(np.mean(data ** 2, axis=1))
emg_dry_filt = notch_filter(bandpass_filter(emg_dry, fs=fs), fs=fs)
emg_wet_filt = notch_filter(bandpass_filter(emg_wet, fs=fs), fs=fs)
rms_dry = compute_rms(emg_dry_filt)
rms_wet = compute_rms(emg_wet_filt)
print("Dry mean RMS:", np.mean(rms_dry))
print("Wet mean RMS:", np.mean(rms_wet))
Additional Tools
Downsampling: Reduce sampling rate for long recordings.
from intan.processing import downsample
downsampled = downsample(emg_data, sampling_rate=fs, target_fs=1000)
Grid Averaging: Compute averages across spatially grouped channels.
from intan.processing import compute_grid_average
grid_avg = compute_grid_average(emg_data, grid_spacing=8)
Summary
These processing tools form the foundation for all subsequent feature extraction and classification tasks using EMG data collected with Intan systems. For details on each function, see the API Reference.