def get_heartrate_and_breathing(self, data): framerate = self.framerate if self.resample != -1: data = resample(data, (len(data) // self.framerate) * self.resample) framerate = self.resample if self.filter: data = hp.filter_signal(data, [0.7, 3.5], sample_rate=framerate, order=3, filtertype='bandpass') data = hp.scale_data(data) wd, m = hp.process(data, framerate, high_precision=True, clean_rr=True) self.m = m self.wd = wd hr = m["bpm"] rr = m["breathingrate"] if self.show: hp.plotter(wd, m) if self.save_path != '': save_plot(data, x_label="Time", y_label="BVP Signal") return hr, rr * 60
def calculate_hr(signal_data, timestamps=None): sampling_rate = 47.63 if timestamps is not None: sampling_rate = hp.get_samplerate_mstimer(timestamps) try: wd, m = hp.process(signal_data, sample_rate=sampling_rate) hr_bpm = m["bpm"] except: hr_bpm = 75.0 if np.isnan(hr_bpm): hr_bpm = 75.0 return hr_bpm else: # We are working with predicted HR: # need to filter and do other stuff.. lets see signal_data = hp.filter_signal(signal_data, cutoff=[0.7, 2.5], sample_rate=sampling_rate, order=6, filtertype='bandpass') try: wd, m = hp.process(signal_data, sample_rate=sampling_rate, high_precision=True, clean_rr=True) hr_bpm = m["bpm"] except: print( "BadSignal received (could not be filtered) using def HR value = 75bpm" ) hr_bpm = 75.0 return hr_bpm
def upsample(data, low, high): # x = np.arange(low, high, 1/1000) # y = np.interp(x, data["Time"], data["PPG"]) sf = len(data)/(high-low) y = hp.filter_signal(data["PPG"], [0.7, 3.5], sample_rate=sf, order=3, filtertype='bandpass') return y
def ppg_preprocessing(data, sampling_rate, low_pass=0.7, high_pass=2.5): filtered = hp.filter_signal(data, [low_pass, high_pass], sample_rate=sampling_rate, order=3, filtertype='bandpass') return filtered
def clean_ppg(data: np.ndarray, sampling_rate: int, low_pass: float=0.7, high_pass: float=2.5): ''' Removes high frequency noises It uses `heartpy <https://github.com/paulvangentcom/heartrate_analysis_python>` library Parameters ----------- data: numpy.ndarray An 1D array of PPG data smpling_rate: int, default: 128 sampling rate low_pass: float, default: 0.7 The low cut frequency for filtering high_pass: float, default: 2.5 The high cut frequency for filtering Returns ------- cleaned_data: numpy.array An 1D array of cleaned PPG data ''' filtered = hp.filter_signal(data, [low_pass, high_pass], sample_rate=sampling_rate, order=3, filtertype='bandpass') return filtered
def clean_ppg(data, sampling_rate, low_pass=0.7, high_pass=2.5): ''' Removes high frequency noises ''' filtered = hp.filter_signal(data, [low_pass, high_pass], sample_rate=sampling_rate, order=3, filtertype='bandpass') return filtered
def analyse_data(self): try: sample_rate = 250 data = hp.get_data('najnowszy2.csv') filtered = hp.filter_signal( data, cutoff=0.05, sample_rate=sample_rate, filtertype='notch') working_data, measures = hp.process(filtered, sample_rate) return data, working_data, measures except hp.exceptions.BadSignalWarning as err: print(err) return None, None, None
def filter_ecg(self): """Applies a 0.5-30Hz, 2nd order bandpass filter to raw data.""" print("\n" + "Filtering data with {}-{}Hz, 2nd order bandpass filter...". format(self.low_f, self.high_f)) # Runs bandpass filter on data self.ecg_filtered = heartpy.filter_signal( data=[float(i) for i in self.ecg_raw], cutoff=[self.low_f, self.high_f], sample_rate=self.signal_frequency, filtertype="bandpass", order=2) print("Filtering complete.")
def filter_signal(filtered, sample_rate, data): """ функция для избавляения от ненужных компонентов, не задевая QRS :param filtered: отфильтрованный ранее сигнал :param sample_rate: шаг :param data: первоначальные данные, используются для сранении при прописовке графиков :return: отфильтрованный сигнал """ filtered = hp.filter_signal(filtered, cutoff=0.05, sample_rate=sample_rate, filtertype='notch') # plt.figure(figsize=(12, 4)) # plt.title('Original and Filtered signal') # plt.plot(data, label='original data') # plt.plot(filtered, alpha=0.5, label='filtered data') # plt.legend() return filtered
def filter_data(time, raw_volt): """This function filters out noise below 10 Hz and above 50Hz This filter takes the time and raw_volt data as input and filters out noises below 10 Hz and above 50 Hz using the heartpy function filter_signal. See documentation on the filter_signal function at: https://python-heart-rate-analysis-toolkit.readthedocs.io/en/ latest/_modules/heartpy/filtering.html Args: time (list): list of time values for the ECG data volts (list): list of ECG voltage magnitudes Returns: list : the filtered ECG voltage values """ logging.info('Filtering Data') sample_rate = 1 / (time[1] - time[0]) volt = hp.filter_signal(raw_volt, [5, 20], sample_rate, 2, 'bandpass') return volt
def get_bpm_estimate(self, time_data, cam_data): fps = hp.get_samplerate_datetime(time_data, '%S.%f') cam_bpm = 0 filtered_ppg_r = hp.filter_signal(cam_data, cutoff=[0.8, 1], filtertype='bandpass', sample_rate=fps, order=4, return_top=False) try: working_data, measures = hp.process(cam_data, fps) except BadSignalWarning: print("Bad signal") else: if (measures['bpm'] > 40 and measures['bpm'] < 180): cam_bpm = measures['bpm'] return filtered_ppg_r, cam_bpm
def run_analysis(self, plot=False): # Filter the signal filtered = hp.filter_signal(self.data, cutoff=0.05, sample_rate=self.sample_rate, filtertype='notch') # Run analysis wd, m = hp.process(hp.scale_data(filtered), self.sample_rate) if plot: # Visualise in plot of custom size plt.figure(figsize=(12, 4)) hp.plotter(wd, m) # Display computed measures for measure in m.keys(): print('%s: %f' % (measure, m[measure])) return m
def process_ecg(data, show=False, framerate=BASE_FREQUENCY): filtered = hp.filter_signal(data, [0.7, 3.5], sample_rate=framerate, order=3, filtertype='bandpass') #filtered = hp.remove_baseline_wander(filtered, FREQUENCY) wd, m = hp.process(hp.scale_data(filtered), framerate) if show: print(wd.keys()) print(m.keys()) hp.plotter(wd, m) save_plot(wd["breathing_signal"], str(framerate) + "breathing_signal_hp.png", framerate, x_label='Time (' + str(framerate) + 'hz)', y_label='Breathing Signal') hr = m["bpm"] rr = m["breathingrate"] RR_list = wd["RR_list"] return hr, rr, RR_list
points = data_influx.get_points() analog_list = [] time_list = [] for p in points: analog_list.append(p['analog']) time_list.append(p['time']) analog_list.reverse() time_list.reverse() data = np.array(analog_list) datetime_data = np.array(time_list) fs = hp.get_samplerate_datetime(datetime_data, timeformat='%Y-%m-%dT%H:%M:%S.%fZ') filtered = hp.enhance_peaks(data, iterations=2) filtered = hp.filter_signal(filtered, cutoff=3, sample_rate=fs, order=2) try: working_data, measures = hp.process(filtered, fs, report_time=True, calc_freq=True) except: print("error calculating RR") sys.exit(0) # hp.plotter(working_data, measures) filtered_body = [] for i in range(0, len(filtered)): point = { "measurement": "sensor_filtered",
def analyze(self, a): # Correct the orientation of the frame. frame = np.fliplr(np.rot90(a)) print('frame.shape = {}'.format(frame.shape)) global camData, camBPMData, psData, psBPMData, time signal_window = frame[self.row:self.row + self.window, self.column:self.column + self.window, :] if (LIGHT_COLOR == 'RED'): signal_window = signal_window[:, :, 0] elif (LIGHT_COLOR == 'BLUE'): signal_window = signal_window[:, :, 1] signal = np.mean(np.mean(signal_window)) # shift data in the arrays one sample left camData[:-1] = camData[1:] camBPMData[:-1] = camBPMData[1:] psData[:-1] = psData[1:] psBPMData[:-1] = psBPMData[1:] time[:-1] = time[1:] camData[-1] = signal time[-1] = (datetime.now() - self.START_TIME).total_seconds() fps = 1 / (time[1] - time[0]) cam_bpm = 0 if (fps < 0): print('negative fps: {}'.format(fps)) elif USE_RT_ANALYSIS: camSig = camData - np.mean(camData) filtered_ppg_r = hp.filter_signal(camSig, cutoff=[0.8, 1], filtertype='bandpass', sample_rate=fps, order=4, return_top=False) try: working_data, measures = hp.process(camSig, 10.0) except BadSignalWarning: print("Bad signal") else: if (measures['bpm'] > 50 and measures['bpm'] < 120): cam_bpm = measures['bpm'] data = pulseOx.get_data() psData[-1] = data['pulseWaveform'] psBPMData[-1] = data['pulseRate'] camBPMData[-1] = cam_bpm if (USE_GUI): ppg_gui.update(self.draw_window(frame), data_dict) writer.save_to_csv(data_dict) tracker.track_performance(False, True) print('(cam_bpm={} , fps={})'.format(int(cam_bpm), int(fps)))
def main(writer): """ A function to extract the bpm from PPG data in a .csv file. Parameters ---------- writer: PPG_Writer A PPG_Writer object initialized with the desired .csv file. """ global bpms, snrs ''' Get/Display raw data and sampling rate. ''' # Acquire time-series data from the .csv file # Ignore the first second of data, since it's very different from the other data. timeData = writer.get_time_data()[30:] camDataRed = writer.get_cam_data_red()[30:] camDataGreen = writer.get_cam_data_green()[30:] pulseData = writer.get_pulseox_data()[30:] # Acquire sampling rate and ground truth BPM from the .csv file sampling_rate = hp.get_samplerate_datetime(timeData, '%S.%f') BPM_TRUTH = writer.get_bpm() # If desired, graph the raw PPG time-series data for both channels. if DISPLAY: plt.title("Raw PPG Data") #plt.plot(timeData, camDataRed, "-r", label="Red") plt.plot(timeData, camDataGreen, "-g", label="Green") plt.legend(loc="lower right") plt.ylabel("Mean Pixel Intensity") plt.xlabel("Time (seconds)") plt.show() ''' Interpolate PPG signals ''' new_fs = 30 sampling_interval = 1 / new_fs # Quantize beginning + ending time to the nearest multiple of sampling_interval. initial_time = int(timeData[0] * new_fs) / new_fs end_time = int(timeData[-1] * new_fs) / new_fs # Figure out number of samples. elapsed_time = end_time - initial_time num_samples = int(elapsed_time * new_fs) + 1 # Generate uniform time scale and interpolation function uniform_time = np.linspace(initial_time, end_time, num=num_samples, endpoint=True) f_red = interp1d(timeData, camDataRed, fill_value="extrapolate") f_green = interp1d(timeData, camDataGreen, fill_value="extrapolate") # Use interpolation function to generate wave. new_red = f_red(uniform_time) new_green = f_green(uniform_time) N = len(new_red) ''' Filter/Enhance Signals ''' # Filter the interpolated PPG signals with a [0.7, 4] Hz bandpass filter. LOWER_CUTOFF = 0.7 UPPER_CUTOFF = 4 new_red = hp.filter_signal(new_red, cutoff=[LOWER_CUTOFF, UPPER_CUTOFF], filtertype='bandpass', sample_rate=new_fs, order=4, return_top=False) new_green = hp.filter_signal(new_green, cutoff=[LOWER_CUTOFF, UPPER_CUTOFF], filtertype='bandpass', sample_rate=new_fs, order=4, return_top=False) # If desired, graph the filtered PPG time-series for both color channels. if DISPLAY: plt.title("Filtered PPG Data") #plt.plot(uniform_time, new_red, '-r', label='Red') plt.plot(uniform_time, new_green, '-g', label='Green') plt.legend(loc="lower right") plt.ylabel("Mean Pixel Intensity") plt.xlabel("Time (seconds)") plt.show() ''' Take FFT of PPGs ''' # Calculate FFT magnitude of both PPG signals. # Remove frequencies above the sampling frequency. fft_red = np.fft.fft(new_red) / N fft_green = np.fft.fft(new_green) / N fft_red = fft_red[range(N // 2)] fft_green = fft_green[range(N // 2)] fft_red = abs(fft_red) fft_green = abs(fft_green) # Get frequency axis corresponding to the FFTs. values = np.arange(N // 2) timePeriod = N * sampling_interval frequencies = values / timePeriod # Find indices corresponding to f_bpm-0.1, f_bpm, and f_bpm+0.1, # Where f_bpm = the ground truth BPM in Hz. f_bpm = BPM_TRUTH / 60 bpm_index = np.abs(frequencies - f_bpm).argmin() upper_index = np.abs(frequencies - f_bpm - 0.1).argmin() lower_index = np.abs(frequencies - f_bpm + 0.1).argmin() # Find the peaks of the FFTs for both color channels. red_peak_index = np.argmax(fft_red) red_bpm = int(frequencies[red_peak_index] * 60) green_peak_index = np.argmax(fft_green) green_bpm = int(frequencies[green_peak_index] * 60) ''' Calculate SNR ''' # Find indices corresponding to the cutoff frequencies of the bandpass filter from earlier. lower_cutoff_index = np.abs(frequencies - LOWER_CUTOFF).argmin() upper_cutoff_index = np.abs(frequencies - UPPER_CUTOFF).argmin() red_signal = 0 red_noise = 0 green_signal = 0 green_noise = 0 # Iterate through all indices between the lower and upper cutoff frequencies for x in range(lower_cutoff_index, upper_cutoff_index + 1): # If current index's frequency is close to the ground truth BPM frequency, # then add its power (magnitude squared) to the signal variable. if (x >= lower_index and x <= upper_index): red_signal = red_signal + fft_red[x]**2 green_signal = green_signal + fft_green[x]**2 # Else, add its power to the noise variable. else: red_noise = red_noise + fft_red[x]**2 green_noise = green_noise + fft_green[x]**2 red_snr = red_signal / red_noise green_snr = green_signal / green_noise print('ppg analysis done') bpms = {'truth': BPM_TRUTH, 'red': red_bpm, 'green': green_bpm} snrs = {'red': red_snr, 'green': green_snr} # If desired, graph the FFTs of both color channels, # with dots indicating the frequencies of the BPM estimates and ground truth. if DISPLAY: ''' plt.title('Red FFT. BPM Estimate = {},Truth = {}'.format(red_bpm, BPM_TRUTH)) plt.plot(frequencies, fft_red) plt.ylabel('Spectral Power') plt.xlabel('Frequency (Hz)') plt.plot(frequencies[red_peak_index], fft_red[red_peak_index], 'ro', label='Estimate') plt.plot(frequencies[bpm_index], fft_red[bpm_index], 'go', label='Reality') plt.legend(loc='upper right') plt.show() ''' plt.title('Green FFT. BPM Estimate = {},Truth = {}'.format( green_bpm, BPM_TRUTH)) plt.plot(frequencies, fft_green) plt.ylabel('Spectral Power') plt.xlabel('Frequency (Hz)') plt.plot(frequencies[green_peak_index], fft_green[green_peak_index], 'ro', label='Estimate') plt.plot(frequencies[bpm_index], fft_green[bpm_index], 'go', label='Reality') plt.legend(loc='upper right') plt.show() # If desired, graph the results from the heart rate variability algorithm, # with the input being the green channel signal, # and plot the pulseox hrv for comparison. if DISPLAY: # PulseOx interpolation and filtering f_pox = interp1d(timeData, pulseData, fill_value="extrapolate") new_pox = hp.filter_signal( f_pox(uniform_time), cutoff=[LOWER_CUTOFF / 4, UPPER_CUTOFF], filtertype='bandpass', sample_rate=new_fs, #sampling_rate order=4, return_top=False) # PulseOx FFT and BPM Estimation fft_pox = abs(np.fft.fft(new_pox) / N) fft_pox = fft_pox[range(N // 2)] pox_bpm = int(frequencies[np.argmax(fft_pox)] * 60) print('pulse ox bpm: {}'.format(pox_bpm)) print('ground truth: {}'.format(BPM_TRUTH)) ''' plt.plot(uniform_time, f_pox(uniform_time)) plt.show() ''' performance_tracker = PerformanceTracker() # Calculate + plot pulse oximeter hrv hrv, thrv, lfcam, hfcam, lf_hfcam, BRcam, sdnncam, sdsdcam, rmssdcam = hrv_function( new_pox, fs=int(30 * BPM_TRUTH / pox_bpm), bpm=BPM_TRUTH) performance_tracker.track_performance() pox_hrv = hrv pox_thrv = thrv print('Pulse Ox Statistics:') print('lfcam: {:.2f}'.format(lfcam)) print('hfcam: {:.2f}'.format(hfcam)) print('lf_hfcam: {:.2f}'.format(lf_hfcam)) print('BRcam: {:.2f}'.format(BRcam)) print('sdnncam: {:.2f}'.format(sdnncam)) print('sdsdcam: {:.2f}'.format(sdsdcam)) print('rmssdcam: {:.2f}'.format(rmssdcam)) print() print() performance_tracker.reset() # Calculate + plot green channel hrv num = int(len(new_green) * pox_bpm / BPM_TRUTH) hrv, thrv, lfcam, hfcam, lf_hfcam, BRcam, sdnncam, sdsdcam, rmssdcam = hrv_function( new_green[:num], fs=30, bpm=green_bpm) performance_tracker.track_performance() print('PPG Statistics:') print('lfcam: {:.2f}'.format(lfcam)) print('hfcam: {:.2f}'.format(hfcam)) print('lf_hfcam: {:.2f}'.format(lf_hfcam)) print('BRcam: {:.2f}'.format(BRcam)) print('sdnncam: {:.2f}'.format(sdnncam)) print('sdsdcam: {:.2f}'.format(sdsdcam)) print('rmssdcam: {:.2f}'.format(rmssdcam)) plt.title("BPM as a function of HRV Algorithm") plt.plot(thrv / 1000, 60000 / hrv, label="ppg") plt.plot(pox_thrv / 1000, 60000 / pox_hrv, label="pulseox") plt.xlabel("Time Window 30s") plt.ylabel("BPM") plt.legend(loc="lower right") plt.show() '''
def main(writer): global bpms, snrs ''' Get/Display raw data and sampling rate. ''' # Ignore the first second of data, since it's very different from the other data. timeData = writer.get_time_data()[30:] camDataRed = writer.get_cam_data_red()[30:] camDataGreen = writer.get_cam_data_green()[30:] pulseData = writer.get_pulseox_data()[30:] sampling_rate = hp.get_samplerate_datetime(timeData, '%S.%f') BPM_TRUTH = writer.get_bpm() if DISPLAY: plt.title("Raw PPG Data") plt.plot(timeData, camDataRed, "-r", label="Red") plt.plot(timeData, camDataGreen, "-g", label="Green") plt.legend(loc="lower right") plt.ylabel("Mean Pixel Intensity") plt.xlabel("Time (seconds)") plt.show() ''' Interpolate PPG signals ''' new_fs = 30 sampling_interval = 1 / new_fs # Quantize time initial_time = int(timeData[0] * new_fs) / new_fs end_time = int(timeData[-1] * new_fs) / new_fs # Figure out number of samples. elapsed_time = end_time - initial_time num_samples = int(elapsed_time * new_fs) + 1 # Generate uniform time scale and interpolation function uniform_time = np.linspace(initial_time, end_time, num=num_samples, endpoint=True) f_red = interp1d(timeData, camDataRed, fill_value="extrapolate") f_green = interp1d(timeData, camDataGreen, fill_value="extrapolate") # Use interpolation function to generate wave. new_red = f_red(uniform_time) new_green = f_green(uniform_time) N = len(new_red) ''' Filter/Enhance Signals ''' # Display the filtered PPG, print the heartpy bpm estimate LOWER_CUTOFF = 0.7 UPPER_CUTOFF = 4 #filtered_red_ppg = hp.filter_signal(camDataRed, new_red = hp.filter_signal( new_red, cutoff=[LOWER_CUTOFF, UPPER_CUTOFF], filtertype='bandpass', sample_rate=new_fs, #sampling_rate order=4, return_top=False) #filtered_green_ppg = hp.filter_signal(camDataGreen, new_green = hp.filter_signal( new_green, cutoff=[LOWER_CUTOFF, UPPER_CUTOFF], filtertype='bandpass', sample_rate=new_fs, #sampling_rate order=4, return_top=False) if DISPLAY: plt.title("Filtered PPG Data") plt.plot(uniform_time, new_red, '-r', label='Red') plt.plot(uniform_time, new_green, '-g', label='Green') #plt.plot(timeData, filtered_red_ppg, "-r", label="Red") #plt.plot(timeData, filtered_green_ppg, "-g", label="Green") plt.legend(loc="lower right") plt.ylabel("Mean Pixel Intensity") plt.xlabel("Time (seconds)") plt.show() ''' Take FFT of PPGs ''' # Take FFT of waves. Remove frequencies above the sampling frequency. fft_red = np.fft.fft(new_red) / N fft_green = np.fft.fft(new_green) / N fft_red = fft_red[range(N // 2)] fft_green = fft_green[range(N // 2)] fft_red = abs(fft_red) fft_green = abs(fft_green) # Get frequency spectrum of FFTs. values = np.arange(N // 2) timePeriod = N * sampling_interval frequencies = values / timePeriod # Find indices of bpm spike. f_bpm = BPM_TRUTH / 60 bpm_index = np.abs(frequencies - f_bpm).argmin() upper_index = np.abs(frequencies - f_bpm - 0.1).argmin() lower_index = np.abs(frequencies - f_bpm + 0.1).argmin() red_peak_index = np.argmax(fft_red) red_bpm = int(frequencies[red_peak_index] * 60) green_peak_index = np.argmax(fft_green) green_bpm = int(frequencies[green_peak_index] * 60) ''' Calculate SNR ''' lower_cutoff_index = np.abs(frequencies - LOWER_CUTOFF).argmin() upper_cutoff_index = np.abs(frequencies - UPPER_CUTOFF).argmin() red_signal = 0 red_noise = 0 green_signal = 0 green_noise = 0 for x in range(lower_cutoff_index, upper_cutoff_index + 1): if (x >= lower_index and x <= upper_index): red_signal = red_signal + fft_red[x]**2 green_signal = green_signal + fft_green[x]**2 else: red_noise = red_noise + fft_red[x]**2 green_noise = green_noise + fft_green[x]**2 red_snr = red_signal / red_noise green_snr = green_signal / green_noise print('ppg analysis done') bpms = {'truth': BPM_TRUTH, 'red': red_bpm, 'green': green_bpm} snrs = {'red': red_snr, 'green': green_snr} if DISPLAY: plt.title('Red FFT. Estimate = {},Truth = {}'.format( red_bpm, BPM_TRUTH)) plt.plot(frequencies, fft_red) plt.ylabel('Spectral Power') plt.xlabel('Frequency (Hz)') plt.plot(frequencies[red_peak_index], fft_red[red_peak_index], 'ro', label='Estimate') plt.plot(frequencies[bpm_index], fft_red[bpm_index], 'go', label='Reality') plt.legend(loc='upper right') plt.show() plt.title('Green FFT. Estimate = {},Truth = {}'.format( green_bpm, BPM_TRUTH)) plt.plot(frequencies, fft_green) plt.ylabel('Spectral Power') plt.xlabel('Frequency (Hz)') plt.plot(frequencies[green_peak_index], fft_green[green_peak_index], 'ro', label='Estimate') plt.plot(frequencies[bpm_index], fft_green[bpm_index], 'go', label='Reality') plt.legend(loc='upper right') plt.show()
_subsetLen = 20 # in seconds _subsetStart = 300000 # SET data_loaded_subset = PPG_data_loaded[_subsetStart:_subsetStart + (_subsetLen * _sampleRate)] plt.figure(figsize=(22, 6)) plt.plot(data_loaded_subset) plt.show() bandpass_low = 1 bandpass_high = 3 filtered_ppg = hp.filter_signal(data_loaded_subset.fillna(0).astype('int32'), cutoff=[bandpass_low, bandpass_high], filtertype='bandpass', sample_rate=_sampleRate, order=3, return_top=False) plt.figure(figsize=(22, 6)) plt.plot(filtered_ppg) plt.show() wd, m = hp.process(filtered_ppg, sample_rate=_sampleRate, high_precision=True, clean_rr=True, clean_rr_method='iqr') plt.figure(figsize=(22, 6)) hp.plotter(wd, m)