def run(inp, opt, cfg): """ http:#ocw.utm.my/file.php/38/SEB4223/07_ECG_Analysis_1_-_QRS_Detection.ppt%20%5BCompatibility%20Mode%5D.pdf """ global hist_ppga, hist_hbi data = arr.interp_undefined(inp['pleth']['vals']) srate = inp['pleth']['srate'] minlist, maxlist = arr.detect_peaks(data, srate) # extract beats beat_res = [{'dt': idx / srate, 'val': 1} for idx in maxlist] ppga_res = [] hbi_res = [] ppga_perc_res = [] hbi_perc_res = [] spi_res = [] for i in range(len(maxlist) - 1): dt = maxlist[i + 1] / srate hbi = (maxlist[i + 1] - maxlist[i]) / srate * 1000 ppga = data[maxlist[i + 1]] - data[minlist[i]] #hbi_perc = hist_hbi.percentile(hbi) * 0.7 + st.norm.cdf(hbi, 754.7, 210.8) * 30 hbi_perc = hist_hbi.percentile(hbi) * 0.7 + st.norm.cdf(hbi, 700, 100) * 30 #ppga_perc = hist_ppga.percentile(ppga) * 0.7 + st.norm.cdf(ppga, 2.428, 1.896) * 30 ppga_perc = hist_ppga.percentile(ppga) * 0.7 + st.norm.cdf( ppga, 1, 0.2) * 30 # hbi_perc = hist_hbi.percentile(hbi) * 0.7 + hist_hbi_grp.percentile(hbi) * 0.3 # ppga_perc = hist_ppga.percentile(ppga) * 0.7 + hist_ppga_grp.percentile(ppga) * 0.3 spi = 100 - (0.7 * ppga_perc + 0.3 * hbi_perc) ppga_res.append({'dt': dt, 'val': ppga}) hbi_res.append({'dt': dt, 'val': hbi}) ppga_perc_res.append({'dt': dt, 'val': ppga_perc}) hbi_perc_res.append({'dt': dt, 'val': hbi_perc}) spi_res.append({'dt': dt, 'val': spi}) hist_hbi.learn(hbi) hist_ppga.learn(ppga) return [beat_res, ppga_res, hbi_res, ppga_perc_res, hbi_perc_res, spi_res]
def run(inp, opt, cfg): """ calculate ppv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ global last_ppv data = arr.interp_undefined(inp['pleth']['vals']) srate = inp['pleth']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: print('hr < 30') return # beat detection minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # beat lengths beatlens = [] beats_128 = [] beats_128_valid = [] for i in range(0, len(minlist) - 1): beatlen = minlist[i + 1] - minlist[i] # in samps if not 30 < beatlen < 300: beats_128.append(None) continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure if not 20 < pp < 100: beats_128.append(None) continue beatlens.append(beatlen) beat = data[minlist[i]:minlist[i + 1]] resampled = arr.resample(beat, 128) beats_128.append(resampled) beats_128_valid.append(resampled) if not beats_128_valid: return avgbeat = np.array(beats_128_valid).mean(axis=0) meanlen = np.mean(beatlens) stdlen = np.std(beatlens) if stdlen > meanlen * 0.2: # irregular rhythm return # remove beats with correlation < 0.9 pulse_vals = [] for i in range(0, len(minlist) - 1): if not beats_128[i]: continue if np.corrcoef(avgbeat, beats_128[i])[0, 1] < 0.9: continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure pulse_vals.append({'dt': minlist[i] / srate, 'val': pp}) # estimates the upper env(n) and lower env(n) envelopes xa = np.array([data[idx] for idx in minlist]) lower_env = np.array([0.0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in minlist]) s = sum(be) if s != 0: lower_env[i] = np.dot(xa, be) / s xb = np.array([data[idx] for idx in maxlist]) upper_env = np.array([0.0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in maxlist]) s = sum(be) if s != 0: upper_env[i] = np.dot(xb, be) / s pulse_env = upper_env - lower_env pulse_env[pulse_env < 0.0] = 0.0 # estimates resp rate rr = arr.estimate_resp_rate(pulse_env, srate) # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist raw_pps = [] pps = [] for ibreath in np.arange(0, m - 1, 0.5): pps_breath = [] for ppe in pulse_vals: if ibreath * nsamp_in_breath < ppe['dt'] * srate < ( ibreath + 1) * nsamp_in_breath: pps_breath.append(ppe['val']) if len(pps_breath) < 4: continue pp_min = min(pps_breath) pp_max = max(pps_breath) ppv = 2 * (pp_max - pp_min) / (pp_max + pp_min) * 100 # estimate if not 0 < ppv < 50: continue # raw_pps.append({'dt': (ibreath * nsamp_in_breath) / srate, 'val': pp}) # # kalman filter if last_ppv == 0: # first time last_ppv = ppv elif abs(last_ppv - ppv) <= 1.0: ppv = last_ppv elif abs(last_ppv - ppv) <= 25.0: # ppv cannot be changed abruptly ppv = (ppv + last_ppv) * 0.5 last_ppv = ppv else: continue # no update pps.append({ 'dt': ((ibreath + 1) * nsamp_in_breath) / srate, 'val': int(ppv) }) return [pps, pulse_vals, [{'dt': cfg['interval'], 'val': rr}]]
ppgas = [] import os idir = r'C:\Users\lucid80\Desktop\SPI_PLETH' #odir = r'C:\Users\lucid80\Desktop\SPI_PLETH' filenames = os.listdir(idir) for filename in filenames: print(filename) ipath = os.path.join(idir, filename) vit = vitalfile.VitalFile(ipath, ['SPI', 'PLETH']) vals = vit.get_samples('PLETH') srate = 100 for istart in range(0, len(vals), 60*srate): data = vals[istart:istart+60*srate] try: minlist, maxlist = arr.detect_peaks(data, 100) # extract beats for i in range(len(maxlist) - 1): hbi = (maxlist[i + 1] - maxlist[i]) / srate * 1000 ppga = data[maxlist[i + 1]] - data[minlist[i]] hbis.append(hbi) ppgas.append(ppga) hist_hbi.learn(hbi) hist_ppga.learn(ppga) except: pass # print('{}\t{}\t{}\t{}'.format(np.mean(hbis), np.std(hbis), np.mean(ppgas), np.std(ppgas))) print(','.join(map(str, hist_hbi.bins))) print(','.join(map(str, hist_ppga.bins))) #import scipy.stats as st
def run(inp, opt, cfg): ecg_data = arr.interp_undefined(inp['ecg']['vals']) ecg_srate = inp['ecg']['srate'] pleth_data = arr.interp_undefined(inp['pleth']['vals']) pleth_srate = inp['pleth']['srate'] pleth_data = arr.band_pass(pleth_data, pleth_srate, 0.5, 15) ecg_rlist = arr.detect_qrs(ecg_data, ecg_srate) pleth_minlist, pleth_maxlist = arr.detect_peaks(pleth_data, pleth_srate) dpleth = np.diff(pleth_data) pleth_dmaxlist = [ ] # index of the maximum slope between peak and nadir in pleth for i in range(len(pleth_minlist)): # maxlist is one less than minlist dmax_idx = arr.max_idx(dpleth, pleth_minlist[i], pleth_maxlist[i + 1]) pleth_dmaxlist.append(dmax_idx) pttmax_list = [] pttmin_list = [] pttdmax_list = [] for i in range(len(ecg_rlist) - 1): if len(pleth_minlist) == 0: continue if len(pleth_maxlist) == 0: continue rpeak_dt = ecg_rlist[i] / ecg_srate rpeak_dt_next = ecg_rlist[i + 1] / ecg_srate if rpeak_dt < cfg['overlap']: continue # find first min in pleth after rpeak_dt in ecg found_minidx = 0 for minidx in pleth_minlist: if minidx > rpeak_dt * pleth_srate: found_minidx = minidx break elif minidx > rpeak_dt_next * pleth_srate: break if found_minidx == 0: continue # find first dmax in pleth after rpeak_dt in ecg found_dmaxidx = 0 for dmaxidx in pleth_dmaxlist: if dmaxidx > rpeak_dt * pleth_srate: found_dmaxidx = dmaxidx break elif dmaxidx > rpeak_dt_next * pleth_srate: break if found_dmaxidx == 0: continue # find first dmax in pleth after rpeak_dt in ecg found_maxidx = 0 for maxidx in pleth_maxlist: if maxidx > rpeak_dt * pleth_srate: found_maxidx = maxidx break elif maxidx > rpeak_dt_next * pleth_srate: break if found_maxidx == 0: continue max_dt = found_maxidx / pleth_srate if max_dt > cfg['interval']: continue min_dt = found_minidx / pleth_srate dmax_dt = found_dmaxidx / pleth_srate pttmax_list.append({'dt': max_dt, 'val': (max_dt - rpeak_dt) * 1000}) pttdmax_list.append({ 'dt': dmax_dt, 'val': (dmax_dt - rpeak_dt) * 1000 }) pttmin_list.append({'dt': min_dt, 'val': (min_dt - rpeak_dt) * 1000}) return [ pttmin_list, pttdmax_list, arr.get_samples(ecg_data, ecg_srate, ecg_rlist), pttmax_list ]
def run(inp, opt, cfg): """ calculate ppv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ data = arr.interp_undefined(inp['pleth']['vals']) srate = inp['pleth']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: return [{}, {}, {}, {}, {}, [], []] minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # estimates the upper ue(n) and lower le(n) envelopes xa = np.array([data[idx] for idx in minlist]) le = np.array([0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in minlist]) s = sum(be) if s != 0: le[i] = np.dot(xa, be) / s xb = np.array([data[idx] for idx in maxlist]) ue = np.array([0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in maxlist]) s = sum(be) if s != 0: ue[i] = np.dot(xb, be) / s re = ue - le re[re < 0] = 0 # estimates resp rate rr = arr.estimate_resp_rate(re, srate) # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist pps = [] for i in range(m - 1): imax = arr.max_idx(re, i * nsamp_in_breath, (i+2) * nsamp_in_breath) # 50% overlapping imin = arr.min_idx(re, i * nsamp_in_breath, (i+2) * nsamp_in_breath) ppmax = re[imax] ppmin = re[imin] ppe = 2 * (ppmax - ppmin) / (ppmax + ppmin) * 100 # estimate if ppe > 50 or ppe < 0: continue pp = cfg['pp'] if pp == 0: pp = ppe err = abs(ppe - pp) if err < 1: pp = ppe elif err < 25: pp = (pp + ppe) / 2 else: pass # dont update cfg['pp'] = pp pps.append({'dt': (i * nsamp_in_breath) / srate, 'val': pp}) return [ [{'dt': cfg['interval'], 'val': rr}], pps ]
def run(inp, opt, cfg): """ calculate ppv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ global last_ppv, last_spv data = arr.interp_undefined(inp['ART']['vals']) srate = inp['ART']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: print('hr < 30') return # beat detection minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # beat lengths beatlens = [] beats_128 = [] beats_128_valid = [] for i in range(0, len(minlist) - 1): beatlen = minlist[i + 1] - minlist[i] # in samps if not 30 < beatlen < 300: beats_128.append(None) continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure if not 20 < pp < 100: beats_128.append(None) continue beatlens.append(beatlen) beat = data[minlist[i]:minlist[i + 1]] resampled = arr.resample(beat, 128) beats_128.append(resampled) beats_128_valid.append(resampled) if not beats_128_valid: return avgbeat = np.array(beats_128_valid).mean(axis=0) meanlen = np.mean(beatlens) stdlen = np.std(beatlens) if stdlen > meanlen * 0.2: # irregular rhythm return # remove beats with correlation < 0.9 pp_vals = [] sp_vals = [] for i in range(0, len(minlist) - 1): if beats_128[i] is None or not len(beats_128[i]): continue if np.corrcoef(avgbeat, beats_128[i])[0, 1] < 0.9: continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure sp = data[maxlist[i]] pp_vals.append({'dt': minlist[i] / srate, 'val': pp}) sp_vals.append({'dt': minlist[i] / srate, 'val': sp}) dtstart = time.time() # estimates resp rate # upper env idx_start = max(min(minlist), min(maxlist)) idx_end = min(max(minlist), max(maxlist)) xa = scipy.interpolate.CubicSpline( maxlist, [data[idx] for idx in maxlist])(np.arange(idx_start, idx_end)) # lower env xb = scipy.interpolate.CubicSpline( minlist, [data[idx] for idx in minlist])(np.arange(idx_start, idx_end)) rr = arr.estimate_resp_rate(xa - xb, srate) dtend = time.time() #print('rr {}'.format(rr)) # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist raw_pps = [] raw_sps = [] ppvs = [] spvs = [] for ibreath in np.arange(0, m - 1, 0.5): pps_breath = [] sps_breath = [] for ppe in pp_vals: if ibreath * nsamp_in_breath < ppe['dt'] * srate < ( ibreath + 1) * nsamp_in_breath: pps_breath.append(ppe['val']) for spe in sp_vals: if ibreath * nsamp_in_breath < spe['dt'] * srate < ( ibreath + 1) * nsamp_in_breath: sps_breath.append(spe['val']) if len(pps_breath) < 4: continue if len(sps_breath) < 4: continue pp_min = min(pps_breath) pp_max = max(pps_breath) sp_min = min(sps_breath) sp_max = max(sps_breath) ppv = (pp_max - pp_min) / (pp_max + pp_min) * 200 if not 0 < ppv < 50: continue spv = (sp_max - sp_min) / (sp_max + sp_min) * 200 if not 0 < spv < 50: continue # kalman filter if last_ppv == 0: # first time last_ppv = ppv elif abs(last_ppv - ppv) <= 1.0: ppv = last_ppv elif abs(last_ppv - ppv) <= 25.0: # ppv cannot be changed abruptly ppv = (ppv + last_ppv) * 0.5 last_ppv = ppv else: continue if last_spv == 0: # first time last_spv = spv elif abs(last_spv - spv) <= 1.0: spv = last_spv elif abs(last_spv - spv) <= 25.0: # ppv cannot be changed abruptly spv = (spv + last_spv) * 0.5 last_spv = spv else: continue ppvs.append(ppv) spvs.append(spv) median_ppv = np.median(ppvs) median_spv = np.median(spvs) return [[{ 'dt': cfg['interval'], 'val': median_ppv }], [{ 'dt': cfg['interval'], 'val': median_spv }], [{ 'dt': cfg['interval'], 'val': rr }]]
def run(inp, opt, cfg): """ calculate svv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ data = arr.interp_undefined(inp['art1']['vals']) srate = inp['art1']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: return [[], [], [], []] minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # make the same length # calculate each beat's std and put it at the peak time stds = [] lzs = [] for i in range(len(minlist) - 1): maxidx = maxlist[i] beat = data[minlist[i]:minlist[i + 1]] if max(beat) - min(beat) < 20: continue s = np.std(beat) stds.append({'dt': maxidx / srate, 'val': s}) sbp = np.max(beat) dbp = beat[0] lz = (sbp - dbp) / (sbp + dbp) # 0.1~0.3 lzs.append({'dt': maxidx / srate, 'val': lz}) # estimates resp rate rr = np.median([o['val'] for o in inp['vent_rr']]) if not rr > 1: return [[], [], [], []] # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist # std svv_stds = [] for i in range(m - 1): # 50% overlapping this_breath_stds = [] for j in range(len(stds)): if i * nsamp_in_breath <= stds[j]['dt'] * srate < ( i + 2) * nsamp_in_breath: this_breath_stds.append(stds[j]['val']) svmax = np.max(this_breath_stds) svmin = np.min(this_breath_stds) svv_stde = 2 * (svmax - svmin) * 100 / (svmax + svmin) # estimate if svv_stde > 40 or svv_stde < 0: continue svv_stds.append(svv_stde) svv_stde = np.median(svv_stds) if svv_stde < 0: svv_stde = 0 svv_std = cfg['svv_std'] if svv_std == 0 or svv_std is None: svv_std = svv_stde err = abs(svv_stde - svv_std) if err < 5: svv_std = svv_stde elif err < 25: svv_std = (svv_std + svv_stde) / 2 else: pass # dont update cfg['svv_std'] = svv_std # lz svv_lzs = [] for i in range(m - 1): # 50% overlapping this_breath_lzs = [] for j in range(len(lzs)): if i * nsamp_in_breath <= lzs[j]['dt'] * srate < ( i + 2) * nsamp_in_breath: this_breath_lzs.append(lzs[j]['val']) svmax = np.max(this_breath_lzs) svmin = np.min(this_breath_lzs) svv_lze = 2 * (svmax - svmin) * 100 / (svmax + svmin) # estimate if svv_lze > 40 or svv_lze < 0: continue svv_lzs.append(svv_lze) svv_lze = np.median(svv_lzs) if svv_lze < 0: svv_lze = 0 svv_lz = cfg['svv_lz'] if svv_lz == 0 or svv_lz is None: svv_lz = svv_lze err = abs(svv_lze - svv_lz) if err < 5: svv_lz = svv_lze elif err < 25: svv_lz = (svv_lz + svv_lze) / 2 else: pass # dont update cfg['svv_lz'] = svv_lz return [ stds, [{ 'dt': cfg['interval'], 'val': svv_std }], lzs, [{ 'dt': cfg['interval'], 'val': svv_lz }] ]