def test_compute_qrs_frames_correlation(ecg_signal=ecg_signal, qrs_frames_none=qrs_frames_none, fs=fs): detectors = Detectors(fs) qrs_frames_swt = detectors.swt_detector(ecg_signal) qrs_frames_hamilton = bsp_ecg.hamilton_segmenter( signal=np.array(ecg_signal), sampling_rate=fs)[0] correlation_coefs = compute_qrs_frames_correlation( qrs_frames_1=qrs_frames_swt, qrs_frames_2=qrs_frames_hamilton, sampling_frequency=fs) assert isinstance(correlation_coefs, float) # test for qrs without length correlation_coefs_none_1 = compute_qrs_frames_correlation( qrs_frames_1=qrs_frames_none, qrs_frames_2=qrs_frames_hamilton, sampling_frequency=fs) assert correlation_coefs_none_1 == 0 correlation_coefs_none_2 = compute_qrs_frames_correlation( qrs_frames_1=qrs_frames_swt, qrs_frames_2=qrs_frames_none, sampling_frequency=fs) assert correlation_coefs_none_2 == 0
def qsqi(ecg_signal: list, sampling_frequency: int) -> float: """Matching Degree of R Peak Detection Two R wave detection algorithms are compared with their respective number of R waves detected. * Hamilton * SWT (Stationary Wavelet Transform) Parameters ---------- ecg_signal : list Input ECG signal sampling_frequency : list Input ecg sampling frequency Returns ------- q_sqi_score : float """ detectors = Detectors(sampling_frequency) qrs_frames_swt = detectors.swt_detector(ecg_signal) qrs_frames_hamilton = bsp_ecg.hamilton_segmenter( signal=np.array(ecg_signal), sampling_rate=sampling_frequency)[0] q_sqi_score = compute_qrs_frames_correlation(qrs_frames_hamilton, qrs_frames_swt, sampling_frequency) return q_sqi_score
def stationary_wavelet_transform_single(fields, record, save_path, sig, save=True): detectors = Detectors(fields['fs']) r_peaks = detectors.swt_detector(sig[:, 0]) if save: save_prediction(r_peaks, record, save_path) else: return r_peaks
def heart_rate_variability(sample, lead, rpeak_method = 'string'): curdir = 'DATA\TrainData_FeatureExtraction' [all_data, header_data, BAD_LABELS] = data_read.data_files_load(curdir) data = all_data[sample][lead] """INITIALIZE DETECTOR CLASS WITH THE SAMPLING RATE:""" detectors = Detectors(500) """FIND RPEAK USING ONE OF THE METHODS BELOW--------------------""" if rpeak_method == 'hamilton' or rpeak_method == 'string': #Hamilton. r_peaks = detectors.hamilton_detector(data) elif rpeak_method == 'christov': #Christov r_peaks = detectors.christov_detector(data) elif rpeak_method == 'engelse': #Engelse and Zeelenberg r_peaks = detectors.engzee_detector(data) elif rpeak_method == 'pan': #Pan and Tompkins r_peaks = detectors.pan_tompkins_detector(data) elif rpeak_method == 'stationary_wavelet': #Stationary Wavelet Transform r_peaks = detectors.swt_detector(data) elif rpeak_method == 'two_moving_average': #Two Moving Average r_peaks = detectors.two_average_detector(data) #elif rpeak_method == 'matched_filter': #Matched Filter #go to pyhrv documentation to find the template file #r_peaks = detectors.matched_filter_detector(data,template_file) """COMPUTE NNI SERIES-------------------------------------------""" nn = nn_intervals(r_peaks) #nni seems to be off by a factor of 3 print("\n\n", nn, "\n\n") """PLOT ECG/TACHOGRAM-------------------------------------------""" #plot_ecg(data, sampling_rate = 500) #tachogram(nn, sampling_rate = 500) """COMPUTE HRV--------------------------------------------------""" results = hrv(nn, None, None, 500) """COMPUTE HR PARAMETERS--(SOMETHING IS WRONG HERE BPM TOO HIGH)""" hr = heart_rate(nn) """COMPUTE FREQUENCY ANALYSIS-----------------------------------""" freq_results = results['fft_bands'] return results, hr, freq_results
def detect_qrs_swt(ecg_data, fs): qrs_frames = [] try: detectors = Detectors(fs) # Explain why qrs_frames = detectors.swt_detector(ecg_data) except Exception: # raise ValueError("swt") print("Exception in detect_qrs_swt") return qrs_frames
hrv_class = HRV(sitting_class.fs) if "e" in sys.argv[1]: ecg_channel_sitting = sitting_class.einthoven_II ecg_channel_maths = maths_class.einthoven_II elif "v" in sys.argv[1]: ecg_channel_sitting = sitting_class.cs_V2_V1 ecg_channel_maths = maths_class.cs_V2_V1 else: print( "Bad argument. Specify 'e' for Einthoven or 'v' for the Chest strap." ) exit(1) r_peaks = detectors.swt_detector(ecg_channel_sitting) sitting_rr_sd.append(hrv_class.RMSSD(r_peaks, True)) r_peaks = detectors.swt_detector(ecg_channel_maths) maths_rr_sd.append(hrv_class.RMSSD(r_peaks, True)) sitting_error_rr = detectors.pan_tompkins_detector(ecg_channel_sitting) sitting_error_rr_sd.append(hrv_class.RMSSD(sitting_error_rr, True)) maths_error_rr = detectors.pan_tompkins_detector(ecg_channel_maths) maths_error_rr_sd.append(hrv_class.RMSSD(maths_error_rr, True)) maths_true_rr = maths_class.anno_cs maths_true_sd.append(hrv_class.RMSSD(maths_true_rr, True)) sitting_true_rr = sitting_class.anno_cs sitting_true_sd.append(hrv_class.RMSSD(sitting_true_rr, True))
def prep_data(self): """Function that: -Initializes ecgdetector class instance -Runs stationary wavelet transform peak detection -Implements 0.1-10Hz bandpass filter -DB3 wavelet transformation -Pan-Tompkins peak detection thresholding -Calculates RR intervals -Removes first peak if it is within median RR interval / 2 from start of window -Calculates average HR in the window -Determines if there are enough beats in the window to indicate a possible valid period """ # Initializes Detectors class instance with sample rate detectors = Detectors(self.fs) # Runs peak detection on raw data ---------------------------------------------------------------------------- # Uses ecgdetectors package -> stationary wavelet transformation + Pan-Tompkins peak detection algorithm self.r_peaks = list(detectors.swt_detector(unfiltered_ecg=self.filt_data)) # List of peak indexes relative to start of data file (i = 0) self.output_r_peaks = [i + self.start_index for i in self.r_peaks] # Checks to see if there are enough potential peaks to correspond to correct HR range ------------------------ # Requires number of beats in window that corresponds to ~40 bpm to continue # Prevents the math in the self.hr calculation from returning "valid" numbers with too few beats # i.e. 3 beats in 3 seconds (HR = 60bpm) but nothing detected for rest of epoch if len(self.r_peaks) >= np.floor(40/60*self.epoch_len): self.enough_beats = True n_beats = len(self.r_peaks) # number of beats in window delta_t = (self.r_peaks[-1] - self.r_peaks[0]) / self.fs # time between first and last beat, seconds self.hr = 60 * (n_beats-1) / delta_t # average HR, bpm # Stops function if not enough peaks found to be a potential valid period # Threshold corresponds to number of beats in the window for a HR of 40 bpm if len(self.r_peaks) < np.floor(40/60*self.epoch_len): self.enough_beats = False self.valid_period = False return # Calculates RR intervals in seconds ------------------------------------------------------------------------- for peak1, peak2 in zip(self.r_peaks[:], self.r_peaks[1:]): rr_interval = (peak2 - peak1) / self.fs self.delta_rr.append(rr_interval) # Approach 1: median RR characteristics ---------------------------------------------------------------------- # Calculates median RR-interval in seconds median_rr = np.median(self.delta_rr) # Converts median_rr to samples self.median_rr = int(median_rr * self.fs) # Removes any peak too close to start/end of data section: affects windowing later on ------------------------ # Peak removed if within median_rr/2 samples of start of window # Peak removed if within median_rr/2 samples of end of window for i, peak in enumerate(self.r_peaks): if peak < (self.median_rr/2 + 1) or (self.epoch_len*self.fs - peak) < (self.median_rr/2 + 1): self.removed_peak.append(self.r_peaks.pop(i)) self.removal_indexes.append(i) # Removes RR intervals corresponding to if len(self.removal_indexes) != 0: self.delta_rr = [self.delta_rr[i] for i in range(len(self.r_peaks)) if i not in self.removal_indexes] # Calculates range of ECG voltage ---------------------------------------------------------------------------- self.volt_range = max(self.raw_data) - min(self.raw_data)
import numpy as np import matplotlib.pyplot as plt import pathlib from ecgdetectors import Detectors current_dir = pathlib.Path(__file__).resolve() example_dir = current_dir.parent / 'example_data' / 'ECG.tsv' unfiltered_ecg_dat = np.loadtxt(example_dir) unfiltered_ecg = unfiltered_ecg_dat[:, 0] fs = 250 detectors = Detectors(fs) #r_peaks = detectors.two_average_detector(unfiltered_ecg) #r_peaks = detectors.matched_filter_detector(unfiltered_ecg) r_peaks = detectors.swt_detector(unfiltered_ecg) #r_peaks = detectors.engzee_detector(unfiltered_ecg) #r_peaks = detectors.christov_detector(unfiltered_ecg) #r_peaks = detectors.hamilton_detector(unfiltered_ecg) #r_peaks = detectors.pan_tompkins_detector(unfiltered_ecg) plt.figure() plt.plot(unfiltered_ecg) plt.plot(r_peaks, unfiltered_ecg[r_peaks], 'ro') plt.title('Detected R-peaks') plt.show()
def ecg_peaks(x, sfreq=1000, new_sfreq=1000, method='pan-tompkins', find_local=True, win_size=100): """A simple wrapper for many popular R peaks detectors algorithms. This function calls methods from the py-ecg-detectors [#]_ module. Parameters ---------- x : list or 1d array-like The oxi signal. sfreq : int The sampling frequency. Default is set to 75 Hz. method : str The method used. Can be one of the following: 'hamilton', 'christov', 'engelse-zeelenberg', 'pan-tompkins', 'wavelet-transform', 'moving-average'. find_local : bool If *True*, will use peaks indexs to search for local peaks given the window size (win_size). win_size : int Size of the time window used by :py:func:`systole.utils.to_neighbour()` Returns ------- peaks : 1d array-like Numpy array containing peaks index. resampled_signal : 1d array-like Signal resampled to the `new_sfreq` frequency. Notes ----- This function will call the py-ecg-detectors package to perform R wave detection. .. warning :: This function will resample the signal to 1000 Hz. Examples -------- >>> from systole import import_dataset >>> from systole.detection import ecg_peaks >>> signal_df = import_dataset()[:20*2000] >>> signal, peaks = ecg_peaks(signal_df.ecg.to_numpy(), method='hamilton', >>> sfreq=2000, find_local=True) >>> print(f'{sum(peaks)} peaks detected.') 24 peaks detected. References ---------- .. [#] Howell, L., Porr, B. Popular ECG R peak detectors written in python. DOI: 10.5281/zenodo.3353396 """ if isinstance(x, list): x = np.asarray(x) # Interpolate f = interp1d(np.arange(0, len(x)/sfreq, 1/sfreq), x, fill_value="extrapolate") time = np.arange(0, len(x)/sfreq, 1/new_sfreq) x = f(time) # Copy resampled signal for output resampled_signal = np.copy(x) detectors = Detectors(new_sfreq) if method == 'hamilton': peaks_idx = detectors.hamilton_detector(resampled_signal) elif method == 'christov': peaks_idx = detectors.christov_detector(resampled_signal) elif method == 'engelse-zeelenberg': peaks_idx = detectors.engzee_detector(resampled_signal) elif method == 'pan-tompkins': peaks_idx = detectors.pan_tompkins_detector(resampled_signal) elif method == 'wavelet-transform': peaks_idx = detectors.swt_detector(resampled_signal) elif method == 'moving-average': peaks_idx = detectors.two_average_detector(resampled_signal) else: raise ValueError( 'Invalid method provided, should be: hamilton,', 'christov, engelse-zeelenberg, pan-tompkins, wavelet-transform,', 'moving-average') peaks = np.zeros(len(resampled_signal), dtype=bool) peaks[peaks_idx] = True if find_local is True: peaks = to_neighbour(resampled_signal, peaks, size=win_size) return resampled_signal, peaks
if sitting_class.anno_cs_exists and maths_class.anno_cs_exists: subject.append(i) hrv_class = HRV(sitting_class.fs) if "e" in sys.argv[1]: ecg_channel = sitting_class.einthoven_II elif "v" in sys.argv[1]: ecg_channel = sitting_class.cs_V2_V1 else: print( "Bad argument. Specify 'e' for Einthoven or 'v' for the Chest strap." ) exit(1) r_peaks = detectors.swt_detector(ecg_channel) sitting_rr_sd.append(hrv_class.RMSSD(r_peaks, True)) r_peaks = detectors.swt_detector(maths_class.cs_V2_V1) maths_rr_sd.append(hrv_class.RMSSD(r_peaks, True)) sitting_error_rr = detectors.two_average_detector(ecg_channel) sitting_error_rr_sd.append(hrv_class.RMSSD(sitting_error_rr, True)) maths_error_rr = detectors.two_average_detector(maths_class.cs_V2_V1) maths_error_rr_sd.append(hrv_class.RMSSD(maths_error_rr, True)) maths_true_rr = maths_class.anno_cs maths_true_sd.append(hrv_class.RMSSD(maths_true_rr, True)) sitting_true_rr = sitting_class.anno_cs sitting_true_sd.append(hrv_class.RMSSD(sitting_true_rr, True))
# all_methods = [res1, res2] # for peaks in all_methods: # peaks = peaks[np.argmax(peaks>plot_begin):np.argmax(peaks>plot_end)] # x = peaks*fs-plot_begin*fs # y = ecg[(peaks*fs).astype(int)] # plt.scatter(x, y) # plt.legend(['ECG', 'Kubios','XQRS']) stop #%% for p in tqdm(ss): fs = p.ecg.sampleRate sig = p.ecg.get_data('ecg').squeeze() r_true = p.get_RR(offset=False)[0] detectors = Detectors(fs) r_pred = np.array(detectors.swt_detector(sig))/fs dist, idxs = compare(r_true, r_pred) x, c = np.unique(np.round(dist, 3), return_counts=True) most_freq = x[np.argmax(c)] print(f'swt : {int(most_freq*1000)}ms') plt.figure() plt.hist(dist, 100) plt.title('swt') plt.xlim(0,0.4) plt.xlabel('diff in sec') plt.ylabel('count')
def run_algo(algorithm: str, sig: numpy.ndarray, freq_sampling: int) -> List[int]: """ run a qrs detector on a signal :param algorithm: name of the qrs detector to use :type algorithm: str :param sig: values of the sampled signal to study :type sig: ndarray :param freq_sampling: value of sampling frequency of the signal :type freq_sampling: int :return: localisations of qrs detections :rtype: list(int) """ detectors = Detectors(freq_sampling) if algorithm == 'Pan-Tompkins-ecg-detector': qrs_detections = detectors.pan_tompkins_detector(sig) elif algorithm == 'Hamilton-ecg-detector': qrs_detections = detectors.hamilton_detector(sig) elif algorithm == 'Christov-ecg-detector': qrs_detections = detectors.christov_detector(sig) elif algorithm == 'Engelse-Zeelenberg-ecg-detector': qrs_detections = detectors.engzee_detector(sig) elif algorithm == 'SWT-ecg-detector': qrs_detections = detectors.swt_detector(sig) elif algorithm == 'Matched-filter-ecg-detector' and freq_sampling == 360: qrs_detections = detectors.matched_filter_detector( sig, 'templates/template_360hz.csv') elif algorithm == 'Matched-filter-ecg-detector' and freq_sampling == 250: qrs_detections = detectors.matched_filter_detector( sig, 'templates/template_250hz.csv') elif algorithm == 'Two-average-ecg-detector': qrs_detections = detectors.two_average_detector(sig) elif algorithm == 'Hamilton-biosppy': qrs_detections = bsp_ecg.ecg(signal=sig, sampling_rate=freq_sampling, show=False)[2] elif algorithm == 'Christov-biosppy': order = int(0.3 * freq_sampling) filtered, _, _ = bsp_tools.filter_signal(signal=sig, ftype='FIR', band='bandpass', order=order, frequency=[3, 45], sampling_rate=freq_sampling) rpeaks, = bsp_ecg.christov_segmenter(signal=filtered, sampling_rate=freq_sampling) rpeaks, = bsp_ecg.correct_rpeaks(signal=filtered, rpeaks=rpeaks, sampling_rate=freq_sampling, tol=0.05) _, qrs_detections = bsp_ecg.extract_heartbeats( signal=filtered, rpeaks=rpeaks, sampling_rate=freq_sampling, before=0.2, after=0.4) elif algorithm == 'Engelse-Zeelenberg-biosppy': order = int(0.3 * freq_sampling) filtered, _, _ = bsp_tools.filter_signal(signal=sig, ftype='FIR', band='bandpass', order=order, frequency=[3, 45], sampling_rate=freq_sampling) rpeaks, = bsp_ecg.engzee_segmenter(signal=filtered, sampling_rate=freq_sampling) rpeaks, = bsp_ecg.correct_rpeaks(signal=filtered, rpeaks=rpeaks, sampling_rate=freq_sampling, tol=0.05) _, qrs_detections = bsp_ecg.extract_heartbeats( signal=filtered, rpeaks=rpeaks, sampling_rate=freq_sampling, before=0.2, after=0.4) elif algorithm == 'Gamboa-biosppy': order = int(0.3 * freq_sampling) filtered, _, _ = bsp_tools.filter_signal(signal=sig, ftype='FIR', band='bandpass', order=order, frequency=[3, 45], sampling_rate=freq_sampling) rpeaks, = bsp_ecg.gamboa_segmenter(signal=filtered, sampling_rate=freq_sampling) rpeaks, = bsp_ecg.correct_rpeaks(signal=filtered, rpeaks=rpeaks, sampling_rate=freq_sampling, tol=0.05) _, qrs_detections = bsp_ecg.extract_heartbeats( signal=filtered, rpeaks=rpeaks, sampling_rate=freq_sampling, before=0.2, after=0.4) elif algorithm == 'mne-ecg': qrs_detections = mne_ecg.qrs_detector(freq_sampling, sig) elif algorithm == 'heartpy': rol_mean = rolling_mean(sig, windowsize=0.75, sample_rate=100.0) qrs_detections = hp_pkdetection.detect_peaks( sig, rol_mean, ma_perc=20, sample_rate=100.0)['peaklist'] elif algorithm == 'gqrs-wfdb': qrs_detections = processing.qrs.gqrs_detect(sig=sig, fs=freq_sampling) elif algorithm == 'xqrs-wfdb': qrs_detections = processing.xqrs_detect(sig=sig, fs=freq_sampling) else: raise ValueError( f'Sorry... unknown algorithm. Please check the list {algorithms_list}' ) cast_qrs_detections = [int(element) for element in qrs_detections] return cast_qrs_detections
def ecg_detector(self, s, detector_type="pan_tompkins"): """ Expose ECG peak detector from the github https://github.com/berndporr/py-ecg-detectors Parameters ---------- s : Input signal fs: The signal frequency. Default is '256 Hz' detector_type: 'hamilton': Open Source ECG Analysis Software Documentation, E.P.Limited, 2002. 'christov':Real time electrocardiogram QRS detection using combined adaptive threshold 'engzee': A single scan algorithm for QRS detection and feature extraction 'swt': Real-time QRS detector using Stationary Wavelet Transform for Automated ECG Analysis. Uses the Pan and Tompkins thresolding. 'mva': Frequency Bands Effects on QRS Detection. 'mtemp': 'pan_tompkins': A Real-Time QRS Detection Algorithm Default = 'pan_tompkins' Returns ------- type an array of 1-D numpy array represent the peak list """ if self.wave_type == 'ppg': warnings.warn("A ECG detectors is using on PPG waveform. " "Output may produce incorrect result") detector = Detectors(self.fs) if detector_type == 'hamilton': res = detector.hamilton_detector(s) elif detector_type == 'christov': res = detector.christov_detector(s) elif detector_type == 'engzee': res = detector.engzee_detector(s) elif detector_type == 'swt': res = detector.swt_detector(s) elif detector_type == 'mva': res = detector.two_average_detector(s) elif detector_type == 'mtemp': res = self.matched_filter_detector(s) else: res = detector.pan_tompkins_detector(s) return np.array(res)