Skip to main content

Clinically-grounded discrete tokenization and per-frame wave segmentation for electrocardiograms

Project description

OpenECG

Clinically-grounded discrete tokenization and per-frame wave segmentation for electrocardiograms.

PyPI Python License

OpenECG ships:

  • A 13-symbol RLE token format (openecg.codec, openecg.vocab) that compresses 12-lead ECGs into a clinically interpretable sequence.
  • A pretrained Conv+Transformer per-frame wave classifier with a parallel boundary-regression head (openecg.stage2) trained on LUDB + QTDB + ISP. Reaches near-SOTA P / QRS / T boundary F1 across all three datasets (see Performance).
  • Loaders and converters for LUDB, QTDB, and ISP datasets so you can reproduce every number in this README.

Install

pip install openecg

PyTorch is a runtime dependency. On CUDA boxes, install the matching wheel first (pip install torch --index-url https://download.pytorch.org/whl/cu124).

Quickstart

from openecg import codec, vocab

# Tokenise a hand-built event stream of (sym_id, length_ms) tuples.
events = [
    (vocab.ID_ISO, 200), (vocab.ID_P, 80),  (vocab.ID_ISO, 80),
    (vocab.ID_Q,   20),  (vocab.ID_R, 40),  (vocab.ID_S, 40),
    (vocab.ID_ISO, 120), (vocab.ID_T, 200), (vocab.ID_ISO, 220),
]
packed = codec.encode(events)              # uint16 array (RLE pack)
print(codec.render_compact(events))        # one char per event
print(codec.render_timed(events, 20))      # char count proportional to ms
print(codec.decode(packed) == events)      # round-trip

For wave segmentation on a real ECG signal (10s, 250 Hz, single lead → per-frame P/QRS/T/other labels), use openecg.stage2.infer.predict_frames after loading a checkpoint with load_model. End-to-end usage: scripts/sota_comparison.py.

Performance

Headline numbers come from the current best checkpoint, stage2_v15_canonical.pt — a Conv+Transformer per-frame classifier with a parallel boundary-regression head and an auxiliary QRS head tapped after the lower 4 transformer layers, whose softmaxed logits are concatenated with the lower features and projected back into the upper transformer's input (Phase 2 of the QRS-first hierarchy). Trained jointly on LUDB + QTDB + ISP + a synthetic AV-block mix that includes Mobitz I / II / complete + paced ventricular escape scenarios. Average F1 across the six P / QRS / T on/off boundaries (Martinez per-boundary tolerances: P 50 ms, QRS 40 ms, T_on 50 ms, T_off 100 ms):

Dataset (eval split) OpenECG v15 OpenECG v13_aux OpenECG v12_reg (legacy) Reference SOTA
LUDB val 0.947 0.953 0.947 DENS-ECG / Moskalenko 2020 ≈ 0.97
ISP test 0.967 0.964 0.966 SemiSegECG 2025 (semi-supervised) ≈ 0.97
QTDB pu0 0.859 0.856 0.847 Martinez 2004 wavelet ≈ 0.97 (T-annotated subset only)
BUT PDB AVB peak F1 0.714 0.680 0.709 — (only public AVB dataset with P labels)

v15 is the first model that improves BUT PDB AVB peak F1 over the v12_reg baseline (+0.005) while also setting new records on ISP and QTDB. The concat path lets the upper layers see the explicit QRS estimate as an input feature — implementing the clinical "find P/T relative to QRS" workflow as an architectural prior. Median boundary timing error is ≤20 ms on every wave on every dataset, meeting the clinical spec target. Full design notes are in docs/superpowers/specs/2026-05-06-v12-postmortem.md; the QTDB +0.020 lift over the original v12_reg came from fix(qtdb): density-based window selection, which corrected a label-window bug that had silenced ~12 % of q1c records during training (scripts/verify_qtdb_fix.py). Run scripts/sota_comparison.py to reproduce per-boundary breakdowns.

Single-lead robustness across the 12 LUDB leads is documented in scripts/per_lead_v4.py; lead III and aVL are the physiologically expected weak spots (small P / T amplitude due to axis orthogonality), which are uncommon as sole monitoring leads in clinical practice.

Reproduce

uv sync
$env:UV_LINK_MODE = "copy"     # Windows + OneDrive workaround
$env:OPENECG_LUDB_ZIP = "<path-to-LUDB-zip>"

uv run pytest                              # unit + stage2 (LUDB integration if env set)

# Train the current best (v15 concat+paced) — needs CUDA, ~1 h on RTX 4090
uv run python scripts/retrain_v15_concat_paced.py  # → data/checkpoints/stage2_v15_concat_paced.pt

# Phase 1 ablation (aux QRS head only, no concat)
uv run python scripts/retrain_v13_aux_qrs.py   # → data/checkpoints/stage2_v13_aux.pt

# Original boundary-only baseline (kept for backward compatibility)
uv run python scripts/train_v12_reg.py     # → data/checkpoints/stage2_v12_reg.pt

# Reproduce the headline table
uv run python scripts/sota_comparison.py   # → out/sota_comparison_*.json

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openecg-0.3.2.tar.gz (114.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openecg-0.3.2-py3-none-any.whl (136.7 kB view details)

Uploaded Python 3

File details

Details for the file openecg-0.3.2.tar.gz.

File metadata

  • Download URL: openecg-0.3.2.tar.gz
  • Upload date:
  • Size: 114.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for openecg-0.3.2.tar.gz
Algorithm Hash digest
SHA256 20b17805eab2a3b73af20031267ebeb1e1c5b0c75942161b5283536b1cfa6abf
MD5 ae062d1cdf2ac3fc761ff578f979b3f7
BLAKE2b-256 baac734c886cbc049531ff1190be4d0de54d6eb5ed45b8b6f292979e2f9de733

See more details on using hashes here.

File details

Details for the file openecg-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: openecg-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 136.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for openecg-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 073547c0010146c0b66382ccc7fa159c2c3f08b6beac583421cd074a74f5380e
MD5 bc350e923aa9b2efdee7e20034b08bd3
BLAKE2b-256 72133e2098f7a1e3c94980da9f1a010f3e370effaad77792e5bbb25ef766ebb4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page