Metadata-Version: 2.4
Name: ecgdeli
Version: 1.0.0
Summary: A faithful, numerically validated Python port of the KIT-IBT ECGdeli MATLAB ECG delineation toolbox
Project-URL: Homepage, https://github.com/xdandys/ecgdeli-py
Project-URL: Repository, https://github.com/xdandys/ecgdeli-py
Project-URL: Issues, https://github.com/xdandys/ecgdeli-py/issues
Project-URL: Upstream, https://github.com/KIT-IBT/ECGdeli
Author: Daniel Strnad
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Keywords: biosignal,delineation,ecg,electrocardiography,qrs
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Requires-Python: >=3.11
Requires-Dist: numpy>=1.26
Requires-Dist: pywavelets>=1.5
Requires-Dist: scipy>=1.11
Provides-Extra: examples
Requires-Dist: matplotlib>=3.8; extra == 'examples'
Description-Content-Type: text/markdown

# ecgdeli-py

A faithful, numerically validated Python port of [**ECGdeli**](https://github.com/KIT-IBT/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](#license-and-citation));
> the Python translation is independent. Please report issues with *this port*
> here — not against the upstream MATLAB repository.

![ECGdeli delineation of the bundled record, lead II — P/QRS/T peaks as dots, on/off boundaries as dashed lines](examples/annotated_ecg.png)

```python
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`](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`](docs/CLINICAL.md)). The design decisions are in
[`docs/DESIGN.md`](docs/DESIGN.md) and the full bug register in [`docs/PARITY.md`](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:

```python
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`](docs/CLINICAL.md)).
The rationale is decision **D1** in [`docs/DESIGN.md`](docs/DESIGN.md); the register is
[`docs/PARITY.md`](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`](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

```bash
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`](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](https://github.com/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`](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](https://doi.org/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`](tests/ATTRIBUTION.md).
