ecg-rectifier / README.md
pashaalex's picture
Add live demo link
0818abd verified
|
Raw History Blame Contribute Delete
3.92 kB
metadata
license: mit
library_name: onnx
pipeline_tag: image-to-image
tags:
  - ecg
  - electrocardiogram
  - document-dewarping
  - onnx

ECG photo rectifier

Turns a phone photo of a paper ECG (a strip or a sheet with one continuous grid; bent, crumpled, folded, held in a hand) into a flat sheet with a square grid at 10 px/mm. CPU only: numpy, OpenCV and onnxruntime; no GPU, no PyTorch.

Live demo: https://huggingface.co/spaces/pashaalex/ECG-Rectifier — upload a photo, get the flat sheet.

git clone https://huggingface.co/pashaalex/ecg-rectifier    # the models need git-lfs or git-xet
cd ecg-rectifier
pip install -r requirements.txt
python3 rectify.py examples/folded.jpg flat.png

Without git: pip install huggingface_hub && hf download pashaalex/ecg-rectifier --local-dir ecg-rectifier.

Run the web demo locally: pip install gradio && python3 app.py.

Examples

Phone photos of printed synthetic ECGs (no patient data), rectified with python3 rectify.py examples/folded.jpg flat.png --illum:

photo flat sheet (flat-illum.jpg; empty corners outside the photo filled with paper colour)
folded strip folded strip, rectified
crumpled strip crumpled strip, rectified

Output

file content
flat.png the flat sheet, 10 px/mm, cropped to the sheet (any OpenCV format; .png is lossless)
flat.json status, reason, self-check, photo scale, timings
flat-check.jpg the photo with the mask (blue), working area (yellow), crop (green) and grid nodes used (red)

Options: --illum also writes flat-illum.jpg with shadows and the lamp tint removed (--illum-paper keep keeps the paper colour); --redshift recolours dark lines red before the networks, for black or grey grids.

Statuses: ok, warn, fail; no_grid (no 5 mm grid found: the masked crop is written without rectification); too_small (sheet < 50 x 30 mm); not_applicable (the grid falls into separate pieces, e.g. Holter reports with framed strips). Exit code 0 means an image was written.

How it works

  1. mask.onnx — a coarse mask of the ECG signal on the whole photo; the photo is cropped to it.
  2. Photo scale (px/mm): the detector is run at several assumed scales; the right one gives a lattice with a 5 mm step, confirmed by four thin 1 mm lines inside each 5 mm cell.
  3. The crop is resized to 5.5 px/mm; detector.onnx finds the intersections of the bold 5 mm lines.
  4. offsets.onnx predicts, for overlapping 224 px windows, where on the sheet (mm) each of 7 x 7 patch centres lies; the windows are fused into one smooth map photo pixel -> sheet mm (least squares).
  5. lattice.onnx (a graph network) numbers the detected nodes (i, j); node (i, j) must lie at (5i, 5j) mm.
  6. The map is corrected by the numbered nodes: a cubic polynomial (or a homography, whichever fits the held-out nodes better), then a smooth local correction table.
  7. The flat sheet is resampled from the original photo by inverting the map.

The self-check fits the correction on half of the nodes (chessboard by i + j) and measures the other half after a best similarity. It measures the shape of the grid, not the absolute scale, and it does not see detector errors; judge the result by eye.

Repository

  • rectify.py — the pipeline; ecglib/ — its building blocks; models/ — the four ONNX networks; examples/ — the photos above; app.py — the web demo (Gradio).
  • training/ — synthetic data generators and training scripts of the networks (see training/README.md); photos are rendered with MeshAugmentor.

License

MIT (LICENSE). The fonts in training/fonts/ keep their own licences (SIL OFL 1.1 for Caveat, Bitstream Vera / DejaVu for DejaVu Sans Mono).