ecgdeli-py
A faithful, numerically validated Python port of ECGdeli (KIT-IBT) — the open-source ECG delineation toolbox for MATLAB. It covers the full filtering and processing pipeline: fiducial-point detection and wave delineation for P, QRS and T.
Unofficial community port. Not affiliated with, endorsed by, or maintained by KIT-IBT. The algorithms are theirs (see citation); the Python translation is independent. Please report issues with this port here — not against the upstream MATLAB repository.
import ecgdeli
filtered = ecgdeli.filter_ecg(signal, fs=1000) # band-pass → notch → isoline
ann = ecgdeli.annotate(filtered, fs=1000) # 1-D → single lead; 2-D (n, leads) → per lead
ann.n_beats # 197
lead = ann.leads[1] # per-lead FPT (0-based, indexes signal directly)
lead.r_peak, lead.qrs_on, lead.t_off, lead.p_on # np.ndarray[int] sample positions per beat
signal[lead.r_peak] # R-peak amplitudes — no ±1 shift needed
The figure above is produced by examples/annotate_example_ecg.py,
which filters, delineates, and plots ECGdeli's own bundled record using only the public API.
Status: stable (v1.0.0). The full filtering and P/QRS/T delineation pipeline, the 0-based
public API, and the p_morphology feature layer. Parity is validated bit-for-bit against MATLAB
R2026a across the corpus, and the delineation is validated clinically against the
QTDB and LUDB cardiologist annotations (see
docs/CLINICAL.md). The design decisions are in
docs/DESIGN.md and the full bug register in docs/PARITY.md.
What "faithful" means here
ECGdeli's MATLAB source contains dozens of real defects (B1–B48 in the register). This port replicates them bit-for-bit by default — that is what gives every result an objective MATLAB ground truth. Fixes are opt-in and individually documented:
ecgdeli.set_fidelity("matlab") # default: bug-for-bug compatible
ecgdeli.set_fidelity("fixed") # opt-in: documented corrections applied
Whether "fixed" is genuinely better is answered with evidence — delineation metrics against the
QTDB and LUDB cardiologist annotations in both modes (docs/CLINICAL.md).
The rationale is decision D1 in docs/DESIGN.md; the register is
docs/PARITY.md.
Indexing: the API is 0-based
signal[fpt.r_peak] works — that is the whole point. The subtlety is that 0 is ECGdeli's "not
detected" sentinel, so a naive −1 shift would turn it into -1, which NumPy reads as the last
sample: a loud failure quietly becomes a plausible number. The public FPT instead carries a
validity mask and safe accessors, and fpt.to_matlab() returns the raw 1-based table. See D4
and "Indexing and the FPT sentinels" in docs/DESIGN.md.
How it was validated
The port was validated bit-for-bit against ECGdeli MATLAB R2026a across the corpus and
clinically against QTDB/LUDB cardiologist annotations. The MATLAB generation rig has since been
retired; the permanent regression net is now a pure-Python characterization oracle — the
current pipeline output, frozen over one record per (db, fs) in both fidelity modes and
re-checked by tests/test_regression.py. Refresh it with make oracle. The committed Tier-1
goldens under tests/goldens/ remain as fast per-stage regression fixtures.
Quick start
git clone https://github.com/xdandys/ecgdeli-py
cd ecgdeli-py
uv sync
make ci # ruff + mypy --strict + pytest
Nothing above needs MATLAB or the network: the Tier-1 goldens and the example ECG the API tests
use (examples/data/) are committed.
Layout
The src/ecgdeli package layout (the faithful core/ transliteration, compat.py, fpt.py) is
documented in docs/DESIGN.md. The rest of the tree:
docs/
DESIGN.md the design decisions (D1, D3, D4, fidelity policy)
PARITY.md the bug register (B1-B48) and deviation log
CLINICAL.md the clinical-validation report
tools/
corpus.toml the human-reviewed corpus SPEC (which records, and why)
corpus.lock.toml the machine-written evidence (resolved IDs + SHA-256s)
freeze_oracle.py freezes the Python characterization oracle (the regression net)
examples/
data/ the bundled example ECG (ECGdeli's own `s0273lre` record)
The upstream ECGdeli MATLAB source is not vendored. The port was made from
KIT-IBT/ECGdeli at commit c3738612 (2025-05-27); clone it
separately if you want to cross-check against the original.
ecgdeli depends only on numpy, scipy and pywavelets. wfdb is a
dev dependency: it fetches the corpus and is never imported by the library.
Authorship
Most of this port was written by an AI model (Claude Opus 4.8), under human direction and review.
The parity gate and the docs/PARITY.md full-database comparison are how that work
was held to the MATLAB original.
License and citation
GPL-3.0-or-later, inherited from ECGdeli. The algorithms are KIT-IBT's — if you use this in research, please cite their paper:
Pilia N., Nagel C., Lenis G., Becker S., Dössel O., Loewe A. ECGdeli — An open source ECG delineation toolbox for MATLAB. SoftwareX 13:100639 (2021). doi:10.1016/j.softx.2020.100639
Test data is redistributed from open-access PhysioNet databases under ODC-BY 1.0
/ CC-BY 4.0; see tests/ATTRIBUTION.md.
Metadata
Release files for ecgdeli 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ecgdeli-1.0.0.tar.gz | 587.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ecgdeli-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 742.0 kB
Release files / ecgdeli-1.0.0.tar.gz
| Download URL | ecgdeli-1.0.0.tar.gz |
|---|---|
| Size | 587.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
056f8d8f3d1c30650587e5c9400b655785ed4a7e3e568a4d4f835c00990e4424
|
|
BLAKE2b-256 checksum How to use checksums |
151beeab81e8a71fac12c77a149396e23fc87469391a279435dbc023571872f0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.13
|
Release files / ecgdeli-1.0.0-py3-none-any.whl
| Download URL | ecgdeli-1.0.0-py3-none-any.whl |
|---|---|
| Size | 154.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
04bebb90706e548cb7944d38ded21f97ba36a6dce40363c5d9fe5ba1b9982d49
|
|
BLAKE2b-256 checksum How to use checksums |
4d80764995450802406797a88dd97755089e887a22b7f65bf5ff04c0167b37ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.13
|