PPG and Remote PPG peak detection

139 visualizaciones (últimos 30 días)
Lena Harrigan
Lena Harrigan el 28 de Dic. de 2022
Respondida: Star Strider el 22 de En. de 2023
Good evening!
First of all excuse me for the long question, I am unexperienced in this field and I just started to study more about it!. I have two signals, one is the PPG signal collected with a sensor (typical pulse oximeter) and the other with a camera, i.e. RPPG (remote). Now I have to process these signals, align them in time and find a correct algorithm to detect the peaks. I have started trying the matlab Toolbox for PPG signal processing(https://ppg-beats.readthedocs.io/en/latest/toolbox/getting_started/) and it is indeed very useful but I think I don't get a good result (maybe because I did not understand how the algorithms work) and I don't know what I am doing wrong (I attach the image. Recording time 175.2398s and 2161 samples).
In addition, I have the signal obtained by the sensor, which curiously is noisier and less 'ideal' than the one collected by the camera and I wanted to know if for both signals can be used, or is optimal, the same code. As a last question (I know it's stupid but I haven't worked in signal processing for a long time and I would like to understand it) how could I align these two signals in time? It is simply to be able to compare them when I process them.
Thank you in advance for your help and excuse me for the long post :) (and happy new year!)
These are my raw signals:
RPPG = load('RPPG.mat')
RPPGSignal = RPPG.ProcessedData.RPPG;
figure
plot(RPPG.ProcessedData.RPPG)
title('RPPG signal')
xlabel('samples')
ylabel('RPPG value')
MonHearthRate=readtable('PPG_sensor.csv');
heartRate= table2array(MonHearthRate);
figure
plot(heartRate)
title('Sensor measurement PPG')
xlabel('samples')
ylabel('PPG value')
And this is my processed RPPG signal according to the matlab toolbox on PPG peak detection:
% setup using the code provided in the question
%readData = csvread('p3_sit.csv',0,0);
%ppg_head = readData(:,1).';
clear all;
clc;
RPPG = load('RPPG.mat')
ppg_head = RPPG.ProcessedData.RPPG;
%ppg_head= RPPGSignal.';
fs = 12;
S.v =ppg_head(:); % extract PPG data
S.fs = 12;
beat_detector = 'IMS'; % Select Incremental-Merge Segmentation beat detector
[peaks, onsets, mid_amps] = detect_ppg_beats(S, beat_detector); % detect beats in PPG
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('RPPG', 'FontSize', ftsize),
xlabel('Time (s)', 'FontSize', ftsize)

Respuestas (1)

Star Strider
Star Strider el 22 de En. de 2023
I went back to have another look at this.
The two signals appear to have very little in common, even with respect to their Fourier transforms.
Defining the sampling frequencies could improve this (I defined the RPPG sampling frequency as:
RPPG_Fs = LRPPG / RPPG_ElapTime; % Sampling Frequency (Hz)
for lack of a better option), and with that it is similar to the ‘PPG’ sampling frequency. Even using resample to equalise the sampling frequencies slightly did not improve the results.
The Fourier transforms of the signals indicates that the ‘RPPG’ instrumentation and existing signal preprocessing may have a ‘highpass’ characteristic of sorts, removing frequencies below about 0.25 Hz that contain a significant amount of information, according to the ‘PPG’ signal Fourier transform.
To me, that points to a fundamental problem of the ‘RPPG’ instrumentation or preprocessing (or both) that would significantly limit its usefulness, especially with respect to clinical applications. Improvements in the instrumentation or initial signal preprocessing could correct for that, however I have no specific suggestions on what those might be, since I have no idea what the preprocessing did to the original ‘RPPG’ signal.
T1 = readtable('https://www.mathworks.com/matlabcentral/answers/uploaded_files/1245967/PPG_sensor.csv');
PPG_Fs = 12; % Sampling Frequency (Hz)
PPG = T1.Wave;
LPPG = numel(PPG)
LPPG = 5187
t_PPG = linspace(0, LPPG-1, LPPG)/PPG_Fs;
LD = load(websave('RPPG','https://www.mathworks.com/matlabcentral/answers/uploaded_files/1245972/RPPG.mat'));
ProcessedData = LD.ProcessedData;
RPPG = ProcessedData.RPPG(:);
RPPG_ElapTime = ProcessedData.ElapsedTime; % Seconds
LRPPG = numel(RPPG);
RPPG_Fs = LRPPG / RPPG_ElapTime; % Sampling Frequency (Hz)
t_RPPG = linspace(0, LRPPG-1, LRPPG)/RPPG_Fs;
[rRPPG,tr_RPPG] = resample(RPPG,t_RPPG,PPG_Fs);
figure
subplot(2,1,1)
plot(t_PPG, PPG, 'DisplayName','PPG')
grid
title('PPG')
ylabel('Amplitude')
subplot(2,1,2)
plot(tr_RPPG, rRPPG, 'DisplayName','RPPG')
grid
title('rRPPG')
xlabel('Time (s)')
ylabel('Amplitude')
LrRPPG = numel(rRPPG);
ixv = (1:LrRPPG); % Equal-Length Vectors (Assume Concurrnet Times)
% figure
% yyaxis left
% plot(t_PPG(ixv), PPG(ixv), 'DisplayName','PPG')
% yyaxis right
% plot(tr_RPPG, rRPPG, 'DisplayName','RPPG')
% grid
% legend('Location','best')
% legend('Location','best')
Fn = PPG_Fs/2;
NFFT = 2^nextpow2(LrRPPG);
FT2 = fft(([rRPPG PPG(ixv)]-mean([rRPPG(:) PPG(ixv)])).*hann(LrRPPG),NFFT)/LrRPPG;
Fv = linspace(0, 1, NFFT/2+1)*Fn;
Iv = 1:numel(Fv);
% figure
% plot(Fv, abs(FT2(Iv,:)*2).*1./max(abs(FT2)))
% grid
% xlabel('Frequency (Hz)')
% ylabel('Magnitude (Amplitude Scaled)')
% legend('rRPPG','PPG(ixv)', 'Location','best')
% xlim([0 2])
figure
subplot(2,1,1)
plot(Fv, abs(FT2(Iv,1)*2))
grid
ylabel('Magnitude')
title('rRPPG')
xlim([0 2])
subplot(2,1,2)
plot(Fv, abs(FT2(Iv,2)*2))
grid
title('PPG(ixv)')
xlabel('Frequency (Hz)')
ylabel('Magnitude')
xlim([0 2])
sgtitle('Fourier Transforms')
.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by