Skip to content

[Phase 2] Signal Processing Pipeline #3

@Sakeeb91

Description

@Sakeeb91

Phase 2: Signal Processing Pipeline

Parent: #1
Depends on: #2

Objectives

Implement robust ECG signal preprocessing pipeline including filtering, baseline removal, and beat segmentation.

Tasks

  • Implement Butterworth bandpass filter (0.5-40 Hz)
  • Implement baseline wander removal using median filter
  • Implement R-peak detection (Pan-Tompkins or neurokit2)
  • Implement beat segmentation (180 samples before/after R-peak)
  • Implement Z-score normalization per beat
  • Write unit tests for each preprocessing step
  • Validate R-peak detection against annotations (>99% sensitivity)

Files to Create/Modify

File Action Description
src/ecg_preprocessing.py Create Main preprocessing class
tests/test_preprocessing.py Create Unit tests

Code Reference

from scipy.signal import butter, filtfilt, medfilt
import numpy as np

class ECGPreprocessor:
    def __init__(self, fs: int = 360):
        self.fs = fs

    def bandpass_filter(self, signal: np.ndarray,
                        lowcut: float = 0.5,
                        highcut: float = 40.0,
                        order: int = 4) -> np.ndarray:
        """Apply zero-phase Butterworth bandpass filter."""
        nyq = 0.5 * self.fs
        low = lowcut / nyq
        high = highcut / nyq
        b, a = butter(order, [low, high], btype='band')
        return filtfilt(b, a, signal)

    def remove_baseline_wander(self, signal: np.ndarray,
                                kernel_size: int = 201) -> np.ndarray:
        """Remove baseline using median filter."""
        baseline = medfilt(signal, kernel_size)
        return signal - baseline

    def detect_r_peaks(self, signal: np.ndarray) -> np.ndarray:
        """Detect R-peaks. Use neurokit2 as fallback."""
        # Implementation here
        pass

    def segment_beats(self, signal: np.ndarray,
                      r_peaks: np.ndarray,
                      before: int = 180,
                      after: int = 180) -> np.ndarray:
        """Extract beats centered on R-peaks."""
        beats = []
        for peak in r_peaks:
            if peak - before >= 0 and peak + after < len(signal):
                beat = signal[peak - before:peak + after]
                beats.append(beat)
        return np.array(beats)

Definition of Done

  • Bandpass filter attenuates frequencies outside 0.5-40 Hz
  • Baseline wander visually removed on test signals
  • R-peak detection sensitivity >99% against MIT-BIH annotations
  • Beat segmentation produces (N, 360) arrays
  • All unit tests pass

Technical Notes

For junior developers:

  • Use filtfilt for zero-phase filtering (no delay)
  • Median filter kernel must be odd number
  • neurokit2.ecg_peaks() is a reliable fallback for R-peak detection
  • Edge beats (near start/end) should be discarded

Risk Mitigation

  • If R-peak detection fails: Use annotated R-peaks from MIT-BIH
  • If filtering distorts QRS: Reduce filter order or adjust cutoffs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions