Remove trend and detect peaks in a photoplethysmogram(PPG) signal
Mostrar comentarios más antiguos
There seems to be a sinusoidal like trend in my PPG signal. I believe this is due to respiration. I would like to detect the PPG peaks(maxima) in this signal after removing the trend or even better detect the peaks without removing the trends. Please help to provide a sample code as I am kind of new to signal processing for bio-signals.
clear all;
clc;
readData = csvread('p3_sit.csv',0,0);
ppg_head = readData(:,1).';
fs = 960;
rec_time_minutes = (length(ppg_head)-1)/60000 %recording time calculation
wn=10/(fs/2); %lowpass 10Hz for ppg
[b,a] = butter(8,wn);
ppg_head_data = filter(b,a,ppg_head);
N1 = length(ppg_head_data);
t1 = [0:N1-1]/fs;
%{comment here %}
figure(1)
plot(t1,ppg_head_data);
xlabel('second');ylabel('Volts');title('PPG Head Signal')
Respuesta aceptada
Más respuestas (1)
Peter H Charlton
el 25 de Nov. de 2022
Editada: Peter H Charlton
el 28 de Dic. de 2022
Since the previous answer, a new toolbox has become available to detect peaks in a photoplethysomogram signal:
After downloading the toolbox and adding it to the Matlab path (as described here), the toolbox can be used to detect peaks in this signal as follows (this is a slight modification of one of the tutorials in the toolbox documentation, here):
% setup using the code provided in the question
readData = csvread('p3_sit.csv',0,0);
ppg_head = readData(:,1).';
fs = 960;
wn=10/(fs/2); %lowpass 10Hz for ppg
[b,a] = butter(8,wn);
ppg_head_data = filter(b,a,ppg_head);
% format data as required by the toolbox
S.v = ppg_head_data(:); % PPG samples in a column vector
S.fs = fs; % sampling frequency in Hz
% detect PPG beats
beat_detector = 'MSPTD'; % specify a beat detection algorithm to use
[peaks, onsets, mid_amps] = detect_ppg_beats(S, beat_detector); % detect beats
% plot the result
figure('Position', [20,20,1000,350]) % Setup figure
subplot('Position', [0.05,0.17,0.92,0.82])
t = [0:length(S.v)-1]/S.fs; % Make time vector
plot(t, S.v, 'b'), hold on, % Plot PPG signal
plot(t(peaks), S.v(peaks), 'or'), % Plot detected beats
ftsize = 20; % Tidy up plot
set(gca, 'FontSize', ftsize, 'YTick', [], 'Box', 'off');
ylabel('PPG', 'FontSize', ftsize),
xlabel('Time (s)', 'FontSize', ftsize)
This produces the following plot, which shows beats detected in the signal:

Zooming in to a 10-second segment:
xlim([10, 20]); h = findobj(gca,'Type','line'); set(h, 'LineWidth', 2)

This plot shows the detected peaks (red circles).
Disclaimer: I'm one of the authors of this PPG beat detection toolbox.
10 comentarios
Patri Valcárcel
el 26 de Dic. de 2022
Good evening,
I'm trying to use your explanation for detecting PPG peaks in signal and I get an error 'Unrecognized function or variable 'detect_ppg_beats' ' where does that function comes from?
Thank you in advance.
Peter H Charlton
el 28 de Dic. de 2022
Thanks for your question.
Please note that the toolbox must be downloaded, and then added to the Matlab path, before using the above code.
I expect this error is due to the toolbox not being visible on the Matlab path (either because it hasn't been downloaded, or because the downloaded files haven't been added to the Matlab path).
I've now added instructions on how to download the files and add them to the Matlab path here. I'll also add these steps to my original answer.
Patricia
el 10 de En. de 2023
Thank you very much for the answer!
Excuse me, it is one of my first times using MATLAB and I am a bit lost. May I ask you something else? I have two signals, one coming from a camera (it is remote PPG, 32FPS) and another one coming from a conventional sensor (so it is typical PPG signal, fs=60) and I have to process them and find the peaks and IBIs on each signal. As I told you it is one of my first times working with these signals and I studied the toolbox to use it but I was wondering if the previous code would also work for my case. I have resampled (downsample) the PPG signal to the remote PPG signal frequency (60 to 32) to have them aligned and be able to compare them but once I try to use the lowpass filter you propose on the previous answers I see no diference on the output signal, moreover, I guess I need more pre-processing than what I am already doing since I think the algorithm is not detecting the peaks correctly (at least on the conventional ppg signal) because of my pre-processing. (And last question, can I use this code for both of the signals? I think it would work with both but I still have that question in mind)
Excuse me for the long question but I am willing to learn more and be able to finish this project! :)
I am attaching the code so far here and the data on the question, thank you in advance!
code:
%remote signal
Signals = load('RPPG.mat')
RPPGSignal = Signals.ProcessedData.RPPG;
fs1= 32;
%sensor signal
MonHearthRate=readtable('PPG_sensor.csv');
heartRate= table2array(MonHearthRate);
PPGSignal= heartRate.';
fs2=60;
%% resampling (downsample the signal coming from the sensor to rPPG (60Hz to 32HZ)
PPGSignal_resampled = (resample(PPGSignal,8,15)).'; %8/15 * 60Hz = 32Hz
PPGSignal_resampled = PPGSignal_resampled.';
%% Preparing the PPG signal
ppg_noOff = detrend(PPGSignal_resampled);
%% Detect peaks on noisy PPG Signals - Only on sensor PPG signal
% setup
fs = 32;
wn=10/(fs/2); %lowpass 10Hz for ppg
[b,a] = butter(8,wn);
ppg_head_data = filter(b,a,y);
% format data as required by the toolbox
S.v = y(:); % PPG samples in a column vector
S.fs = fs; % sampling frequency in Hz
% detect PPG beats
beat_detector = 'MSPTD'; % specify a beat detection algorithm to use
[peaks, onsets, mid_amps] = detect_ppg_beats(S, beat_detector); % detect beats
% plot the result
figure('Position', [20,20,1000,350]) % Setup figure
subplot('Position', [0.05,0.17,0.92,0.82])
t = [0:length(S.v)-1]/S.fs; % Make time vector
plot(t, S.v, 'b'), hold on, % Plot PPG signal
plot(t(peaks), S.v(peaks), 'or'), % Plot detected beats
ftsize = 20; % Tidy up plot
set(gca, 'FontSize', ftsize, 'YTick', [], 'Box', 'off');
ylabel('PPG', 'FontSize', ftsize),
xlabel('Time (s)', 'FontSize', ftsize)
Peter H Charlton
el 10 de En. de 2023
Editada: Peter H Charlton
el 12 de En. de 2023
This code seems to work. Change 'do_remote' to zero to process the PPG sensor signal.
%% Load data
%remote signal
Signals = load('RPPG.mat');
RPPGSignal = Signals.ProcessedData.RPPG;
fs1= 32;
%sensor signal
MonHearthRate=readtable('PPG_sensor.csv');
heartRate= table2array(MonHearthRate);
PPGSignal= heartRate.';
fs2 = 60;
%% format data as required by the toolbox
do_remote = 1;
if do_remote
S.v = RPPGSignal;
S.fs = fs1; % sampling frequency in Hz
else
S.v = PPGSignal;
S.fs = fs2;
end
%% detect PPG beats
beat_detector = 'MSPTD'; % specify a beat detection algorithm to use
[peaks, onsets, mid_amps] = detect_ppg_beats(S, beat_detector); % detect beats
%% plot the result
figure('Position', [20,20,1000,350]) % Setup figure
subplot('Position', [0.05,0.17,0.92,0.82])
t = [0:length(S.v)-1]/S.fs; % Make time vector
plot(t, S.v, 'b'), hold on, % Plot PPG signal
plot(t(peaks), S.v(peaks), 'or'), % Plot detected beats
ftsize = 20; % Tidy up plot
set(gca, 'FontSize', ftsize, 'YTick', [], 'Box', 'off');
ylabel('PPG', 'FontSize', ftsize),
xlabel('Time (s)', 'FontSize', ftsize)
Patricia
el 10 de En. de 2023
Thank you again! :) So should I not pre-process the signal? And is there any option on the toolbox to calculate the IBIs?
Peter H Charlton
el 10 de En. de 2023
I don't know whether pre-processing the signals would improve beat detection. In our recent article on PPG beat detection we pre-processed the signals by bandpass filtering between 0.67 and 8 Hz, which could help.
Inter-beat intervals (IBIs) can be calculated as follows:
ibis = diff(t(peaks));
Patricia
el 12 de En. de 2023
Hi again! the previous code gives me an error on the detect_ppg_beats as 'Too many input arguments' By what can this be caused?
Peter H Charlton
el 12 de En. de 2023
Good point - now corrected
Patricia
el 12 de En. de 2023
Thank you! :) And would you recommed to use the preprocess_ppg_signal function as a method to prepare the signal? Or just the bandpass filtering between 0.67 and 8 Hz?
Patricia
el 20 de En. de 2023
Hi again and excuse me for reopening the question. I was trying the performance assessment of the detector using the 'capnobase' option and once I get it working I do not get any result as a return, stops at this point:

any idea on what can I be doing wrong?
Thank you again.
Categorías
Más información sobre Parametric Modeling en Centro de ayuda y File Exchange.
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!



