File size: 21,056 Bytes
e4d73f9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | # β‘ ECGLight: ECG Digitization & Classification Dashboard
<!--
<table align="center" border="0" style="border-collapse: collapse; border: none;">
<tr style="border: none;">
<td align="center" valign="middle" style="border: none; padding-right: 40px;">
<img src="assets/logo.png" alt="ECG Digitization & Classification Logo" width="180px" style="border-radius: 14px; box-shadow: 0 4px 16px rgba(15, 23, 42, 0.08);" />
</td>
<td align="center" valign="middle" style="border: none;">
<img src="assets/scai_lab_logo.svg" alt="SCAI Lab Logo" height="100px" />
</td>
</tr>
</table>
-->
An advanced, interactive Streamlit web workstation designed to convert printed/photographed 12-lead paper ECG reports into high-resolution digitized signals and carry out different types of classification over them. The suite is engineered to run with low computational resource requirements, operating seamlessly on a standard consumer laptop GPU (via CUDA) or running completely on CPU.
## <a id="table-of-contents"></a>π Table of Contents
- [π₯οΈ Web Dashboard Workstation Overview](#web-dashboard-workstation-overview)
- [π Installation & Setup](#installation-setup)
- [π§ Pre-Trained Classifiers & Tasks](#pre-trained-classifiers-tasks)
- [π Command Line Usage](#command-line-usage)
- [π Repository Structure & Directory Organization](#repository-structure-directory-organization)
- [π· How It Works: Signal Digitization](#how-it-works-signal-digitization)
- [π How It Works: Signal Analysis & Visualization](#how-it-works-signal-analysis-visualization)
- [β‘ How It Works: Heartbeat Segmentation](#how-it-works-heartbeat-segmentation)
- [π§ How It Works: Cardiac Classification](#how-it-works-cardiac-classification)
- [π€ Collaborating Institutions](#collaborating-institutions)
- [π Citation](#citation)
- [π₯ Authors & Contact](#authors-contact)
- [π License](#license)
## <a id="web-dashboard-workstation-overview"></a>π₯οΈ Web Dashboard Workstation Overview
The dashboard provides a premium, responsive user interface designed for research, education, and clinical workflow exploration. It coordinates the digitization and classification pipelines into a unified, lightweight web application.
### Workstation Modules
1. **π· ECG Image Digitizer**:
- Upload any scanned or photographed ECG image (`.png`, `.jpg`, `.jpeg`).
- Run the sequential YOLOv11 pipeline step-by-step with real-time progress indicators.
- Outputs a summary of detected leads and total samples.
- Automatically saves the digitized CSV to disk under `output/digitization/latest_digitized.csv` for downstream consumption.
2. **π ECG Signal Viewer**:
- Visualizes multi-channel ECG signals interactively using client-side native Streamlit line charts (supporting zoom, pan, and hover tooltips).
- Supports stacked subplots (with distinct clinical colors for each lead: clinical red, teal, deep blue, yellow, purple, etc.) or overlaid graphs.
- Displays statistical summaries (mean, standard deviation, min/max, range) and allows row-by-row signal previewing.
3. **β€οΈ ECG Classification**:
- Predicts cardiac conditions using pre-trained ensemble and deep learning classifiers.
- Automatically segments raw signals into heartbeats around R-peaks using the Pan-Tompkins algorithm before running inference.
- **Inference Mode (No Ground-Truth)**: If the uploaded CSV lacks diagnostic labels, the dashboard displays a downloadable **Predictions Table** detailing predicted class diagnoses and model confidence probabilities.
- **Evaluation Mode (With Ground-Truth)**: If labels are present, the page calculates and plots performance metrics (Accuracy, F1-Score, Sensitivity, Specificity, Confusion Matrix).
### π Workflow
The workstation coordinates the pipeline through four distinct steps: **Digitization**, **Analysis**, **Segmentation**, and **Classification**. The technical workflows for each process are documented below in their respective **How It Works** sections.
---
## <a id="installation-setup"></a>π Installation & Setup
### Prerequisites
- Python 3.9
- CUDA-capable GPU recommended (automatically falls back to CPU if unavailable).
### Conda Environment & Model Setup
1. Clone the repository and navigate to the project directory:
```bash
git clone https://github.com/scai-lab/ECG-Digitization-Classification.git
cd ECG-Digitization-Classification
```
2. Create the conda environment using the provided `environment.yml` configuration:
```bash
conda env create -f environment.yml
conda activate infer
```
> [!IMPORTANT]
> **Windows Compatibility & TensorFlow Setup**:
> If you are on Windows and encounter native runtime loading failures (`ImportError: DLL load failed while importing _pywrap_tensorflow_internal: A dynamic link library (DLL) initialization routine failed`), you need to install a stable version pairing of TensorFlow and Protobuf:
> ```bash
> pip install tensorflow==2.15.0 protobuf==4.25.3
> ```
> *(Make sure no background Streamlit or Python tasks are running when executing this command, to prevent file locking issues on `.pyd` libraries).*
3. **Download Pre-Trained Model Weights**:
Due to their file sizes, the YOLO detection checkpoints and pre-trained classifiers are hosted externally. Download the `models/` directory from the link below and place it directly in the root of the project:
π **[Download Pre-Trained Models Directory (ETH ZΓΌrich Polybox)](https://polybox.ethz.ch/index.php/s/GDACstPtsoTrrWH)**
Once extracted, verify that the weights are located inside the directory tree structure:
```text
models/
βββ digitization_models/
β βββ yolo11_full/weights/best.pt
β βββ yolo11_lead/weights/best.pt
β βββ yolo11_pulse/weights/best.pt
β βββ yolo11_patch/weights/best.pt
βββ classifier_models/
βββ mi_vs_normal_segmented/
βββ omi_vs_nonomi/
βββ ecg_surgery/
```
Key packages installed by the environment: `torch 2.7`, `ultralytics 8.3`, `opencv-python 4.11`, `scikit-image 0.24`, `wfdb 4.3`, `patched-yolo-infer 1.3.8`, `sktime`, `streamlit`.
---
## <a id="pre-trained-classifiers-tasks"></a>π§ Pre-Trained Classifiers & Tasks
The classification engine supports three diagnostic tasks using the pre-trained weights in `classifier_models/`:
| Classification Task | Model Type | Expected Input Shape | Test Accuracy | Positive Class |
| :--- | :--- | :--- | :---: | :--- |
| **Normal vs Myocardial Infarction (MI) - Segmented** | Arsenal | 12 leads Γ 140 timesteps | **92.3%** | `MYOCARDIAL_INFARCTION` |
| **Occlusive MI (OMI) vs non-OMI** | Rocket | 12 leads Γ 141 timesteps | **88.9%** | `OMI` |
| **Pre-Procedural vs Post-Procedural MI** | InceptionTime | 12 leads Γ 140 timesteps | **91.4%** | `pre-procedural MI` |
- **Arsenal**: An ensemble of ROCKET classifiers utilizing random convolutional kernels to extract feature representations combined with ridge regression.
- **Rocket**: Random Omni-directional Kernel Extraction (ROCKET) classifier, computing kernel convolutions quickly for high-dimensional time-series data.
- **InceptionTime**: A deep convolutional network ensemble modeled on the Inception architecture, extracting multi-scale temporal features.
---
## <a id="command-line-usage"></a>π Command Line Usage
### Run Batch Digitization (`run_org.py`)
The batch processing script processes nested hospital directories, exporting structured folders of digitized CSVs:
1. Configure path variables at the top of `run_org.py`:
```python
ORGANIZED_DIR = "../ecg_files/ECG_organized_all" # Input dataset root
OUTPUT_DIR = "../ecg_files/ECG_digitized" # Mirrored CSV directory
CATEGORIES = ["pre", "index", "post"] # Categories to process
```
2. Run the script:
```bash
python run_org.py
```
### Run Model Inference (`run_inference.py`)
Execute predictions directly on digitized data from the command line using `run_inference.py` located in `archive/classification/`:
```bash
# MI vs Normal Segmented heartbeat classification
python archive/classification/run_inference.py --model mi_vs_normal_segmented --input data/ptb_xl/segmented_heartbeats.csv
# OMI vs non-OMI classification
python archive/classification/run_inference.py --model omi_vs_nonomi --input data/ecg_matrix_omi_segmented_50_150_90.csv
# Custom output file path
python archive/classification/run_inference.py --model ecg_surgery --input data/ecg_surgery_segmented_50_150_70.csv --output results/surgery_preds.csv
```
---
## <a id="repository-structure-directory-organization"></a>π Repository Structure & Directory Organization
The repository is structured to maintain a clean root directory, moving utility runners, UI views, model checkpoints, and legacy/training scripts into distinct modules:
```
.
βββ app.py # Streamlit application main router
βββ config.py # Centralized configuration and model registry
βββ digitization.py # Core ECGImage extraction pipeline class
βββ environment.yml # Conda environment dependency file
βββ README.md # Comprehensive repository documentation
β
βββ backend/ # Dashboard background execution adapters
β βββ __init__.py # Backend package declaration
β βββ digitization_runner.py # YOLO loader and single-image processor
β βββ classification_runner.py # Pre-trained model loader and preprocessor
β
βββ utils/ # Streamlit front-end page components
β βββ __init__.py # Utils package declaration
β βββ branding.py # Sidebar titles, headers, and footer logos
β βββ css.py # Custom clinical theme and grid background CSS
β βββ hardware.py # Displays CPU/GPU hardware properties (cached)
β βββ page_digitizer.py # Front-end for the ECG Digitizer page
β βββ page_csv_viewer.py # Front-end for the interactive Signal Viewer
β βββ page_classifier.py # Front-end for the Classification workstation
β
βββ models/ # Relocated YOLO checkpoints and classifiers
β βββ digitization_models/ # YOLO v11 checkpoints for digitization
β β βββ yolo11_full/ # YOLO Bounding boxes
β β βββ yolo11_lead/ # YOLO Lead names
β β βββ yolo11_pulse/ # YOLO Reference pulses
β β βββ yolo11_patch/ # YOLO Waveform segmentations
β β
β βββ classifier_models/ # Bundled pre-trained diagnostic classifiers
β βββ mi_vs_normal_segmented/ # Pre-trained Arsenal model (segmented beats)
β βββ omi_vs_nonomi/ # Pre-trained Rocket model (segmented beats)
β βββ ecg_surgery/ # Pre-trained InceptionTime model (segmented beats)
β
βββ archive/ # Archived developer, training, and legacy scripts
βββ classification/
βββ train_and_save_models.py# Script used to compile pre-trained models
βββ run_inference.py # Independent CLI inference execution script
βββ run_classification.py # Baseline MLP classifier pipeline
βββ run_benchmarking.py # Comparison benchmarking suite
βββ run_lead_importance_test.py# Individual lead performance evaluator
βββ feature_analysis.py # Original all-in-one analysis script
βββ aggregate_subject_metrics.py# Multi-subject performance aggregator
βββ dataset_curate.py # Local dataset curation utility
βββ re_plotter.py # Advanced Gaussian signal generator & visualizer
```
---
## <a id="how-it-works-signal-digitization"></a>π· How It Works: Signal Digitization
The core class [digitization.py](file:///d:/Projects/ECGLight/digitization.py) operates a multi-stage sequential computer vision pipeline to translate raster images into digitized signals:
```mermaid
graph TD
A[ECG Image Upload] --> B[Preprocessing: Otsu & Blurring]
B --> C[YOLOv11 Detection & Segmentation]
subgraph YOLOv11 Models
C1[yolo11_full: Lead Boundaries]
C2[yolo11_lead: Text Name Labels]
C3[yolo11_pulse: Calibration Pulses]
C4[yolo11_patch: Waveform Segments]
end
C --> C1 & C2 & C3 & C4
C1 & C2 & C3 & C4 --> D[Hough Lines Calibration]
D --> E[K-Means Row & Column Grid Construction]
E --> F[Anti-Leakage Connected Components Filter]
F --> G[Centroid Trace & Resampling to 500Hz]
G --> H[Export latest_digitized.csv]
```
1. **Preprocessing**: Cleans the scanned image using shadow-removal masks, Otsu binarization, and Gaussian blurring to isolate ink lines from paper textures.
2. **YOLO Segmentation**: Applies a patched YOLO segmentation model at three crop scales (`4Γ`, `4.5Γ`, and `5Γ` height) to isolate individual lead waveform contours.
3. **Sequential Detections**: Runs three YOLO models in parallel:
- `yolo11_full`: Bounding boxes for the 12 lead channels.
- `yolo11_lead`: Text labels representing lead names (I, II, aVR...).
- `yolo11_pulse`: Bounding boxes for the calibration reference pulses (typically 1mV high, representing vertical scale).
4. **Scale Calibration**: Fits Hough lines to the calibration pulse boundaries. The pixel height determines the voltage scale (`volt/pixel`), while the width determines the time scale (`time/pixel`).
5. **Grid Construction**: Employs K-Means clustering on lead coordinates to map rows and columns, automatically parsing standard Cabrera orders and grid formats (3Γ4, 4Γ3, 6Γ2, 12Γ1).
6. **Signal Extraction & Post-Processing**: Traces contours to extract raw pixel centroids, performs baseline correction, applies linear interpolation to bridge gaps, and resamples to a standard **500 Hz** frequency calibrated in **millivolts (mV)**. In addition, an **anti-leakage component filter** is executed per cell crop using connected components analysis to automatically identify the primary waveform trace and strip out smaller, boundary-adjacent components (leaked signals from neighboring leads) that sit far from the row baseline.
---
## <a id="how-it-works-signal-analysis-visualization"></a>π How It Works: Signal Analysis & Visualization
Once continuous signals are extracted, the dashboard runs analytical tasks and displays interactive previews:
```mermaid
graph TD
A[Upload Digitized CSV] --> B[Parse Lead Voltages & Timestamps]
B --> C[Vega-Lite Interactive Visualizer]
C --> C1[Render Stacked Leads]
C --> C2[Render Overlaid Signals]
B --> D[Compute Signal Statistics: Mean, SD, Min/Max]
D --> E[Display Summary Dataframes & Row Previews]
```
1. **Parse Signals**: Reads digitized CSV format, validating lead names and timestamps.
2. **Vega-Lite Visualization**: Renders interactive charts supporting native client-side zoom, pan, and hover tooltips for all channels.
3. **Signal Statistics**: Automatically computes statistical characteristics (mean, standard deviation, min/max values) for each lead.
---
## <a id="how-it-works-heartbeat-segmentation"></a>β‘ How It Works: Heartbeat Segmentation
To prepare continuous digitized signals for the classification models, the pipeline runs the Pan-Tompkins R-peak detection algorithm:
```mermaid
graph TD
A[Digitized 500Hz Signal] --> B[Bandpass Filter 5-15Hz]
B --> C[Derivative Filter]
C --> D[Squaring Operation]
D --> E[Moving Window Integration]
E --> F[Adaptive Thresholding & R-Peak Search]
F --> G[Extract 140-sample Beats: 50ms pre-R, 150ms post-R]
G --> H[Max-Absolute Voltage Normalization]
```
1. **Filtering**: The Lead II signal is filtered via a bandpass filter (5-15 Hz) to suppress muscle noise, baseline wander, and T-wave interference.
2. **Differentiation**: Computes the slope of the signal to highlight the rapid change in the QRS complex.
3. **Squaring**: Performs point-by-point squaring to amplify QRS slopes while attenuating smaller waves.
4. **Integration**: A moving window integrator (typically 150ms wide) compiles the slope information into a peak window.
5. **Adaptive Thresholding & Peak Search**: Dynamically computes threshold constants based on average noise and signal levels, locating R-peaks.
6. **Beat Windowing**: Extracts a localized heartbeat around each R-peak (typically extending 50ms before and 150ms after the peak), normalizes the voltage per heartbeat using max-absolute scaling, and truncates/pads the resulting segments to the target model input width (e.g. 140 or 141 timesteps).
---
## <a id="how-it-works-cardiac-classification"></a>π§ How It Works: Cardiac Classification
The heartbeat segment tensors are evaluated using pre-trained time-series classification models:
```mermaid
graph TD
A[Segmented Heartbeats] --> B[Numpy3D Reshaping: N_instances Γ 12_leads Γ N_timesteps]
B --> C[Select Classification Task]
subgraph Model Registry
C1[Normal vs MI: Arsenal]
C2[OMI vs non-OMI: Rocket]
C3[Pre vs Post-Procedural MI: InceptionTime]
end
C --> C1 & C2 & C3
C1 & C2 & C3 --> D[Load Pre-Trained Pickled Estimator]
D --> E[Predict Class Labels & Probabilities]
E --> F[Generate Downloadable Predictions CSV]
```
1. **Numpy3D Formatting**: Formats the heartbeat segments into a standard `sktime` `Numpy3D` tensor with shape `(N_instances, 12_leads, N_timesteps)`.
2. **Dynamic Task Selection**: Loads the pre-trained pickled model corresponding to the selected classification task.
3. **Model Inference**: Evaluates the model to compute class predictions and probability confidences.
4. **Result Generation**: Automatically builds downloadable prediction tables and calculates performance metrics if ground-truth labels are present in the dataset.
---
## <a id="collaborating-institutions"></a>π€ Collaborating Institutions
This project was developed in collaboration with:
- [ETH ZΓΌrich](https://ethz.ch)
- [Istituto Cardiocentro Ticino (EOC)](https://www.cardiocentro.org)
- [UniversitΓ della Svizzera italiana (USI)](https://www.usi.ch)
- [UniversitΓ della Campania Luigi Vanvitelli](https://www.unicampania.it)
<p align="center">
<a href="https://ethz.ch" target="_blank">
<img src="assets/ETH_ZΓΌrich_Logo_black.svg.png" alt="ETH ZΓΌrich" height="30px" style="vertical-align: middle; margin: 0 15px;" />
</a>
<a href="https://www.cardiocentro.org" target="_blank">
<img src="assets/eoc_logo.png" alt="Istituto Cardiocentro Ticino (EOC)" height="35px" style="vertical-align: middle; margin: 0 15px;" />
</a>
<a href="https://www.usi.ch" target="_blank">
<img src="assets/usi_logo.png" alt="USI" height="35px" style="vertical-align: middle; margin: 0 15px;" />
</a>
<a href="https://www.unicampania.it" target="_blank">
<img src="assets/Logo_Vanvitelli_university.svg.png" alt="UniversitΓ della Campania Luigi Vanvitelli" height="35px" style="vertical-align: middle; margin: 0 15px;" />
</a>
</p>
---
## <a id="citation"></a>π Citation
```bibtex
@article{natraj2026ecglight,
title={ECGLight: Compute-Light Framework For Paper ECG Digitization and Myocardial Infarction Screening},
author={Natraj, Shreyasvi and Achtari, Cyrus and Gragnano, Felice and Milzi, Andrea and Valgimigli, Marco and Paez-Granados, Diego},
journal={arXiv preprint arXiv:2607.07683},
year={2026},
url={https://arxiv.org/abs/2607.07683},
doi={10.48550/arXiv.2607.07683}
}
```
---
## <a id="authors-contact"></a>π₯ Authors & Contact
- **Shreyasvi Natraj** β [snatraj@ethz.ch](mailto:snatraj@ethz.ch)
- **Cyrus Achtari**
---
## <a id="license"></a>π License
This project is released under the **Non-Commercial Academic and Research License Agreement**. Please refer to the [LICENSE](file:///d:/Projects/ECGLight/LICENSE) file in the repository root for the full licensing terms. The codebase and trained model weights are provided free of charge for personal, academic, and non-profit research use only. Commercial use is strictly prohibited.
|