def sqi2(self): """linear resampling (between two fiducials up to length L, correlation)""" slicez, islicez = self.slices(method='variable') L = len(self.template) corrs = np.array([(cross_corr(sig_resample(sl, L), self.template) if len(sl) else 0.0) for sl in slicez]) corrs = np.clip(corrs, a_min=0.0, a_max=1.0) return corrs
def sqi3(self): """DTW resampling (resampling to length L and correlation)""" slicez, islicez = self.slices(method='variable') corrs = np.array([ cross_corr(*self.dtw_resample(sl)) if len(sl) else 0.0 for sl in slicez ]) corrs = np.clip(corrs, a_min=0.0, a_max=1.0) return corrs
def sqi1(self): """direct matching (fiducial + length L template correlation)""" # nb. slight difference: we are centering the window on the beat, while Li et al slicez, islicez = self.slices(method='fixed') corrs = np.array([ cross_corr(sl, self.template) if len(sl) else 0.0 for sl in slicez ]) corrs = np.clip(corrs, a_min=0.0, a_max=1.0) return corrs
def ecg_kept(raw): sig0 = raw fps = sig0.fps # print 'slice.x shape=', sig0.slice(slice(int(0*fps), int(ECG_DURATION*fps))).x.shape ### ecg = beatdet_ecg(sig0.slice(slice(int(0 * fps), int(ECG_DURATION * fps)))) ecg.lead = LEADS[ECG_LEAD] # scaling for plot pcs = np.array([np.percentile(ecg.x, 10), np.percentile(ecg.x, 90)]) pcs = (pcs - np.mean(pcs)) * 5.0 + np.mean(pcs) ### slicez = sqi_slices(ecg, method='fixed', slice_front=0.5, slice_back=-0.5) L = max([len(sl) for sl in slicez]) padded_slicez = np.array( [sig_pad(sl, L, side='center', mode='constant') for sl in slicez]) ### perc = envelopes_perc_threshold(padded_slicez) le, ue = envelopes_at_perc(padded_slicez, perc) mb = np.median(padded_slicez, axis=0) bpt = beat_penalty_threshold(le, ue, mb) bps = np.array([beat_penalty(sl, le, ue, mb) for sl in padded_slicez]) bp_ok = bps < bpt ### template = np.median(padded_slicez, axis=0) corrs = np.array([cross_corr(sl, template) for sl in padded_slicez]) CORR_THRESHOLD = 0.8 corr_ok = corrs > CORR_THRESHOLD ### # next beat is OK as well? bp_ibi_ok = bp_ok & np.roll(bp_ok, -1) corr_ibi_ok = corr_ok & np.roll(corr_ok, -1) igood = np.where(bp_ibi_ok & corr_ibi_ok)[0]
def beat_template_1(self): self.L = np.median(np.diff(self.tbeats)) slicez_1, islicez_1 = self.slices( method="fixed") #, hwin=int(self.L*self.fps/2.))) self.ibis_good = islicez_1 template_1 = np.mean(slicez_1, axis=0) #print 'template_1', template_1 slicez, islicez = self.slices(method="fixed", scrub=False) corrs = np.array([cross_corr(sl, template_1) for sl in slicez]) self.slicez_1, self.islicez_1, self.template_1 = slicez_1, islicez_1, template_1 self.slicez, self.islicez, self.corrs = slicez, islicez, corrs # TODO: penalize the correlation of overlong IBIs (since correlation only goes until the beat template ends) # debug: put shape envelopes self.env_min = sqi_copy_to_idxs(self.s_min, len(self.x), self.ibeats.astype(int), [sqi_slice_norm(sl) for sl in slicez]) self.env_max = sqi_copy_to_idxs(self.s_max, len(self.x), self.ibeats.astype(int), [sqi_slice_norm(sl) for sl in slicez])
def beat_detect(self, debug=False, outlierthreshold=0.001): # Heuristics: # * found enough beats # * median ibi is in plausible range (or even: some percentile of ibis is in plausible range) # * histogram of beat correlations is plausible (lots of good correlation) # # Good beats have positive correlation, e.g. rho > 0.5 with the median beat. ecg = self.ecg #ecg.x[:int(ecg.fps*5)] *= 0.0 # avoid the terrible swing # # Kim ECG beat detection # smoothsignal = np.array(ecg.x) # kill outliers mn, mx = np.min(smoothsignal), np.max(smoothsignal) m = min(abs(mn), abs(mx)) N = 100 step = m / float(N) for i in range(N): n = len(np.where(smoothsignal < -m)[0]) + len( np.where(smoothsignal > m)[0]) if n > outlierthreshold * len(smoothsignal): break m -= step mn, mx = -m, m smoothsignal[smoothsignal < mn] = mn smoothsignal[smoothsignal > mx] = mx smoothsignal[-10:] = 0 # extreme outlier in last few frames # adjust distribution to the one Kim has optimized for smoothsignal = (smoothsignal - np.mean(smoothsignal) ) / np.std(smoothsignal) * 0.148213 - 0.191034 loc, beattime = self.QRSdetection(smoothsignal, ecg.fps, ecg.t, ftype=0) loc = loc.flatten() # # check error Kim vs. localmax of R peaks # new_loc = localmax_climb(ecg.x, loc, hwin=int(0.02 * ecg.fps)) # hwin = 20 ms peak_errs = (new_loc - loc) / float(ecg.fps) #print 'np.mean(peak_errs), np.std(peak_errs)', np.mean(peak_errs), np.std(peak_errs) ibis = np.diff(loc / float(ecg.fps)) median_ibi = np.median(ibis) # # filter beats by cross-correlation with median beat # ecg_slices = np.array( slices(ecg.x, loc, hwin=int(np.ceil(median_ibi * ecg.fps)) // 2)) # median value from each timepoint (not a single one of any of the beats) median_beat = np.median(ecg_slices, axis=0) if debug: plt.plot(np.arange(len(median_beat)) / float(ecg.fps), median_beat) plt.title('median ECG beat') cross_corrs = [cross_corr(sl, median_beat) for sl in ecg_slices] spectrum_ok = np.array( [self.slice_good(sl, median_beat) for sl in ecg_slices]) ccs_ok = np.array(cross_corrs) > NoisyECG.GOOD_BEAT_THRESHOLD good_loc_idxs = np.where(ccs_ok & spectrum_ok)[0] if debug: [ plt.plot( np.arange(len(ecg_slices[i])) / float(ecg.fps), ecg_slices[i]) for i in range(1, len(ecg_slices)) if i in good_loc_idxs ] plt.title('all good ECG beats with rho > {:.2f}'.format( NoisyECG.GOOD_BEAT_THRESHOLD)) plt.show() beat_idxs = loc[good_loc_idxs] beat_times = beattime[good_loc_idxs] self._beattime, self._cross_corrs = beattime, cross_corrs if debug: self.debug_plot() #self.cross_corrs = np.array(cross_corrs)[good_loc_idxs] self.median_ibi = median_ibi self.good_beat_fraction = float(len(good_loc_idxs)) / len(cross_corrs) return beat_idxs, beat_times