In [1]:
import numpy as np
import skimage.io
import skimage.filters
import skimage.color
import skimage.segmentation
import skimage.util
import skimage.restoration
import skimage.feature
import pandas as pd
import scipy.interpolate

import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = (20.0, 20.0)

In [2]:
#load raw data
fname='../ekg-m.png'
img = skimage.io.imread(fname)
img = skimage.color.rgb2gray(img)

plt.imshow(img)


Out[2]:
<matplotlib.image.AxesImage at 0x7f7dfd4a3d90>

In [3]:
#denoise (ele edge detection is no good)
imgf = skimage.util.img_as_float(img)
denoised = skimage.restoration.denoise_tv_bregman(imgf, 15.2)  # Go Bruins!
#denoised = skimage.restoration.nl_means_denoising(imgf,patch_size=5, patch_distance=12)
plt.imshow(denoised)


Out[3]:
<matplotlib.image.AxesImage at 0x7f7dfd521c50>

In [4]:
#find edges, overlay on original image
edges = skimage.feature.canny(denoised,sigma=2)
plt.imshow(edges + img, cmap=plt.cm.gray)


Out[4]:
<matplotlib.image.AxesImage at 0x7f7dfce5c250>

In [5]:
#use the edges to get a 1D signal out of the 2D one
xs = []
ys = []
for x in range(img.shape[1]):
    y = np.median(np.where(edges[100:400,x])[0])
    y = int(y) if not np.isnan(y) else None
    if y:
        ys.append(y)
        xs.append(x)
        
f = scipy.interpolate.interp1d(xs,ys)
line = [f(x)+100 for x in range(min(xs), max(xs))]
line = scipy.signal.wiener(line,3)

hline = np.ones_like(line)*(np.median(line))
plt.plot(hline,c='b')


plt.plot(line,c='r')
plt.imshow(img+edges,cmap=plt.cm.gray)
plt.savefig('ekg-plotted.png')


/home/aahu/.virtualenvs/ekg/local/lib/python2.7/site-packages/numpy/core/_methods.py:59: RuntimeWarning: Mean of empty slice.
  warnings.warn("Mean of empty slice.", RuntimeWarning)

In [6]:
#trial/error to figure pixels per box... TODO statistics etc..
#I'm going to guess 55px per box (heart rate is ~300bpm?!)
#Looking at photos on crashingpatient.com suggests https://en.wikipedia.org/wiki/Supraventricular_tachycardia
box = img[20:71,0:280]
box = box[:,20:]
plt.imshow(box)
PIX_PER_BOX=55.0



In [7]:
#resample to time domain and save 
#40px is 0.2 seconds => .005 seconds per pixel in x-direction
#40px is 0.5 mV => .0125 mV per pixel in y-direction
#use median as baseline (Does this make sense?)
df = pd.DataFrame(data=line,columns=['ekg'])
df['sec'] = [(0.2/PIX_PER_BOX)*x for x in range(len(df))]
df['ekg'] = (df['ekg'] - np.median(df['ekg']))*(0.5/PIX_PER_BOX)

df.to_csv('../ekg.tsv', index=False, sep='\t')

In [8]:
df


Out[8]:
ekg sec
0 1.210213 0.000000
1 0.537885 0.003636
2 0.375627 0.007273
3 0.431502 0.010909
4 0.616961 0.014545
5 0.678657 0.018182
6 0.528645 0.021818
7 -0.149057 0.025455
8 -0.179334 0.029091
9 0.200847 0.032727
10 0.205930 0.036364
11 0.036233 0.040000
12 -0.130434 0.043636
13 -0.284980 0.047273
14 -0.227404 0.050909
15 -0.191040 0.054545
16 -0.136495 0.058182
17 -0.172858 0.061818
18 -0.215283 0.065455
19 -0.263767 0.069091
20 -0.366798 0.072727
21 -0.433464 0.076364
22 -0.506192 0.080000
23 -0.413680 0.083636
24 -0.301734 0.087273
25 -0.278774 0.090909
26 -0.682810 0.094545
27 -0.755705 0.098182
28 0.360528 0.101818
29 0.305389 0.105455
... ... ...
963 0.102899 3.501818
964 0.121081 3.505455
965 0.096839 3.509091
966 0.187748 3.512727
967 0.142293 3.516364
968 0.118051 3.520000
969 0.129124 3.523636
970 0.338951 3.527273
971 0.466536 3.530909
972 0.487748 3.534545
973 0.505930 3.538182
974 0.548354 3.541818
975 0.624111 3.545455
976 0.736233 3.549091
977 0.854414 3.552727
978 0.954414 3.556364
979 0.981066 3.560000
980 -0.075967 3.563636
981 -0.054736 3.567273
982 0.479994 3.570909
983 0.505930 3.574545
984 0.507201 3.578182
985 0.864676 3.581818
986 -0.502350 3.585455
987 -0.530434 3.589091
988 -0.514272 3.592727
989 -0.491040 3.596364
990 -0.460737 3.600000
991 -0.543565 3.603636
992 -0.762663 3.607273

993 rows × 2 columns


In [ ]: