MediumHow I Build End-to-End Pipelines for ECG, DICOM, and Echocardiography Data in Python | by Farhatbasir | Medium
Sitemap

How I Build End-to-End Pipelines for ECG, DICOM, and Echocardiography Data in Python

4 min readJun 15, 2026

--

By Farhat Basir — Biomedical Engineer | Available for freelance work on Upwork

Press enter or click to view image in full size

If you’ve ever worked with biomedical data, you know the gap between “I have this ECG/DICOM/echo dataset” and “I have a working, reproducible analysis pipeline” is enormous — and almost nobody talks about what actually goes wrong in between.

This article walks through the exact workflow I use across all my projects: biosignal processing, medical image analysis, and deep learning for clinical data. Whether you’re a researcher with ECG data you can’t figure out how to preprocess, or a clinician sitting on a DICOM archive with no idea where to start — this is for you.

Step 1: Loading and Parsing the Data

The first mistake most people make is treating biomedical data like a CSV. It isn’t.

For ECG/EEG/PPG/GSR signals, I use Python’s wfdb (for PhysioNet datasets), scipy.io for MATLAB .mat files, or neurokit2 for general biosignal loading. The first thing I always check:

import wfdb
import numpy as np
record = wfdb.rdrecord('mitdb/100')
signal = record.p_signal[:, 0] # Lead I
fs = record.fs # sampling frequency — critical
print(f"Sampling rate: {fs} Hz | Duration: {len(signal)/fs:.1f}s")

Sampling rate matters more than anything else. If you don’t know your fs, your entire feature extraction will be wrong.

For DICOM / echocardiography, I use pydicom:

import pydicom
import numpy as np
ds = pydicom.dcmread('echo_file.dcm')
pixel_array = ds.pixel_array # shape: (frames, height, width) for cine echo
print(f"Frames: {pixel_array.shape[0]} | Size: {pixel_array.shape[1:]}")

Echocardiography DICOMs are typically multi-frame (cine loops). Don’t treat them like static images.

Step 2: Preprocessing — This is Where Most Projects Break

For ECG:

import neurokit2 as nk
# Bandpass filter (0.5–40 Hz is standard for ECG)
ecg_filtered = nk.ecg_clean(signal, sampling_rate=fs, method='biosppy')
# R-peak detection
_, rpeaks = nk.ecg_peaks(ecg_filtered, sampling_rate=fs)
r_peak_indices = rpeaks['ECG_R_Peaks']

The two biggest artifacts I see: baseline wander (fix with a high-pass filter at 0.5 Hz) and powerline interference (notch filter at 50 or 60 Hz depending on your country).

For DICOM echocardiography:

import cv2
frames = []
for i in range(pixel_array.shape[0]):
frame = pixel_array[i]
# Normalize to 0–255
frame_norm = cv2.normalize(frame, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
# CLAHE for contrast enhancement
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
frame_enhanced = clahe.apply(frame_norm)
frames.append(frame_enhanced)

Step 3: Feature Extraction — Hand-Crafted First, Then Deep Learning

My approach is always hybrid: extract meaningful clinical features by hand first, then let a CNN extract global patterns, then fuse both. This is the architecture I used in my current PeerJ submission for MI classification from echocardiography.

Get Farhatbasir’s stories in your inbox

Join Medium for free to get updates from this writer.

Hand-crafted features (ECG example):

# HRV features from R-peaks
rr_intervals = np.diff(r_peak_indices) / fs * 1000 # in milliseconds
features = {
'mean_rr': np.mean(rr_intervals),
'sdnn': np.std(rr_intervals),
'rmssd': np.sqrt(np.mean(np.diff(rr_intervals)**2)),
'heart_rate': 60000 / np.mean(rr_intervals)
}

Deep learning global features (PyTorch):

import torch
import torchvision.models as models
# Load pretrained ResNet, remove final classification layer
backbone = models.resnet50(pretrained=True)
backbone = torch.nn.Sequential(*list(backbone.children())[:-1])
backbone.eval()
# Extract global feature vector from echo frame
with torch.no_grad():
frame_tensor = preprocess(frame).unsqueeze(0)
global_features = backbone(frame_tensor).squeeze() # 2048-dim vector

Fusion:

# Concatenate hand-crafted + deep features
hand_features = torch.tensor(list(features.values()), dtype=torch.float32)
fused = torch.cat([hand_features, global_features])

Step 4: Model Training and Evaluation

I always use GitHub + Google Colab for reproducibility. Every experiment is version-controlled, and the Colab notebook runs cleanly without any local setup required by the client.

from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import roc_auc_score
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
aucs = []
for fold, (train_idx, val_idx) in enumerate(skf.split(X, y)):
# ... train model, evaluate
auc = roc_auc_score(y_val, y_pred_proba)
aucs.append(auc)
print(f"Fold {fold+1} AUC: {auc:.4f}")
print(f"Mean AUC: {np.mean(aucs):.4f} ± {np.std(aucs):.4f}")

Step 5: Visualization and Explainability

For medical imaging, results mean nothing without interpretability. I always include Grad-CAM visualizations so the clinician can see where the model is looking:

from pytorch_grad_cam import GradCAM
from pytorch_grad_cam.utils.image import show_cam_on_image
cam = GradCAM(model=model, target_layers=[model.layer4[-1]])
grayscale_cam = cam(input_tensor=input_tensor)
visualization = show_cam_on_image(rgb_img, grayscale_cam[0])

Step 6: Statistical Analysis and Writeup

After modelling, every result needs proper statistical validation — not just accuracy scores. I run confidence intervals, paired t-tests or Wilcoxon signed-rank tests (depending on distribution), and report all metrics (sensitivity, specificity, AUC, F1) per class.

The writeup — methodology, results, and Draw.io pipeline figures — is the last step. Most researchers underestimate how much time this takes. I’ve found that having a clear block diagram of your pipeline (built in Draw.io) dramatically speeds up the methodology section and gets you through reviewer comments faster.

Work With Me

If you’re stuck at any stage of this pipeline — or need someone to build the whole thing from scratch — I’m available on Upwork.

I work exclusively with biomedical signals and medical images. ECG, EEG, PPG, GSR, DICOM, echocardiography, MRI. If that’s your data, message me.

Farhat Basir | Biomedical Engineer | Portfolio | LinkedIn

Tags: ECG processing Python · DICOM medical imaging · echocardiography deep learning · biomedical signal processing · medical image segmentation · PyTorch medical imaging · biosignal feature extraction · EEG classification · cardiac imaging AI

--

--