Skip to content

Commit cbc63ff

Browse files
committed
Finished converting to physio inputs
1 parent d96c1a5 commit cbc63ff

File tree

2 files changed

+72
-26
lines changed

2 files changed

+72
-26
lines changed

physioqc/metrics/chest_belt.py

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99

1010
from .. import references
1111
from ..due import due
12-
from .utils import hamming, physio_or_numpy
12+
from .utils import hamming
1313

1414

1515
def envelopedetect(data, upperpass=0.05, order=5):
1616
"""
17-
17+
Detects the amplitude envelope of a pseudoperiodic waveform (respiration, cardiac, etc.)
1818
Parameters
1919
----------
2020
data
@@ -55,30 +55,80 @@ def envelopedetect(data, upperpass=0.05, order=5):
5555
return einferior, esuperior
5656

5757

58+
def respiratorysqi(rawresp, debug=False):
59+
"""
60+
Calculate the breath by breath quality of the respiratory signal
61+
Parameters
62+
----------
63+
rawresp: physio_like
64+
The raw respiratory signal
65+
debug: bool
66+
Print debug information and plot intermediate results
67+
68+
Returns
69+
-------
70+
breathlist: list
71+
List of "breathinfo" dictionaries for each detected breath. Each breathinfo dictionary contains:
72+
"startime", "centertime", and "endtime" of each detected breath in seconds from the start of the waveform,
73+
and "correlation" - the Pearson correlation of that breath waveform with the average waveform.
74+
75+
"""
76+
targetfs = 25.0
77+
prefilterlimits = [0.01, 2.0]
78+
seglength = 12.0
79+
segstep = 2.0
80+
envelopelpffreq = 0.05
81+
slidingfilterpctwidth = 10.0
82+
83+
return romanosqi(
84+
rawresp,
85+
targetfs=targetfs,
86+
prefilterlimits=prefilterlimits,
87+
envelopelpffreq=envelopelpffreq,
88+
slidingfilterpctwidth=slidingfilterpctwidth,
89+
seglength=seglength,
90+
segstep=segstep,
91+
debug=debug,
92+
)
93+
94+
5895
@due.dcite(references.ROMANO_2023)
59-
def respiratorysqi(
60-
rawresp, Fs, envelopelpffreq=0.075, slidingfilterpctwidth=10.0, debug=False
96+
def romanosqi(
97+
rawresp,
98+
targetfs=25.0,
99+
prefilterlimits=(0.01, 2.0),
100+
envelopelpffreq=0.05,
101+
slidingfilterpctwidth=10.0,
102+
seglength=12.0,
103+
segstep=2.0,
104+
debug=False,
61105
):
62106
"""
63107
Implementation of Romano's method from A Signal Quality Index for Improving the Estimation of
64108
Breath-by-Breath Respiratory Rate During Sport and Exercise,
65109
IEEE SENSORS JOURNAL, VOL. 23, NO. 24, 15 DECEMBER 2023
66110
67111
NB: In part A, Romano does not specify the upper pass frequency for the respiratory envelope filter.
68-
0.075Hz looks pretty good.
112+
0.05Hz looks pretty good for respiration.
69113
In part B, the width of the sliding window bandpass filter is not specified. We use a range of +/- 5% of the
70114
center frequency.
71115
72116
Parameters
73117
----------
74-
rawresp: ndarray
118+
rawresp: physio_like
75119
The raw respiration signal
76-
Fs: float
77-
The sampling frequency of the respiration signal in Hz
120+
targetfs: float
121+
Sample rate for internal calculations
122+
prefilterlimits: tuple
123+
Lower and upper frequency limits for prefiltering the input signal
78124
envelopelpffreq: float
79125
Cutoff frequency of the RMS envelope filter in Hz
80126
slidingfilterpctwidth: float
81127
Width of the sliding window bandpass filter in percent of the center frequency
128+
seglength: float
129+
Length of the window for measuring the waveform center frequency in seconds
130+
segstep: float
131+
Step size of the window for measuring the waveform center frequency in seconds
82132
debug: bool
83133
Print debug information and plot intermediate results
84134
@@ -87,20 +137,17 @@ def respiratorysqi(
87137
breathlist: list
88138
List of "breathinfo" dictionaries for each detected breath. Each breathinfo dictionary contains:
89139
"startime", "centertime", and "endtime" of each detected breath in seconds from the start of the waveform,
90-
and "correlation" - the Pierson correlation of that breath waveform with the average waveform.
140+
and "correlation" - the Pearson correlation of that breath waveform with the average waveform.
91141
"""
92-
# rawresp = physio_or_numpy(rawresp)
93142

94143
# get the sample frequency down to around 25 Hz for respiratory waveforms
95-
targetfs = 25.0
96-
rawresp = Physio(rawresp, fs=Fs)
97144
rawresp = operations.interpolate_physio(rawresp, target_fs=targetfs)
98145

99146
# A. Signal Preprocessing
100147
# Apply third order Butterworth bandpass, 0.01-2Hz
101148
prefiltered = operations.filter_physio(
102149
rawresp,
103-
[0.01, 2.0],
150+
prefilterlimits,
104151
"bandpass",
105152
order=3,
106153
)
@@ -147,10 +194,8 @@ def respiratorysqi(
147194
)
148195
plt.show()
149196

150-
# B. Detection of breaths in sliding window
151-
seglength = 12.0
197+
# B. Detection of peaks in sliding window
152198
segsamples = int(seglength * targetfs)
153-
segstep = 2.0
154199
stepsamples = int(segstep * targetfs)
155200
totaltclength = len(rawresp)
156201
numsegs = int((totaltclength - segsamples) // stepsamples)
@@ -298,19 +343,17 @@ def plotbreathqualities(breathlist, totaltime=None):
298343
plt.show()
299344

300345

301-
def plotbreathwaveformwithquality(waveform, breathlist, Fs, plottype="rectangle"):
346+
def plotbreathwaveformwithquality(data, breathlist, plottype="rectangle"):
302347
"""
303348
Make an informational plot of the respiratory waveform with the quantifiability of each detected breath as a
304349
color overlay.
305350
306351
Parameters
307352
----------
308-
waveform: ndarray
353+
data: physio_like
309354
The respiratory waveform
310355
breathlist: list
311356
A list of breathinfo dictionaries output from respiratorysqi
312-
Fs: float
313-
The sampling frequency of the respiratory waveform in Hz
314357
plottype: str
315358
The type of plot to make. If plottype is rectangle (default), overlay a colored rectangle to show the
316359
quantifiability of each detected breath. If anything else, use the waveform line color to indicate the
@@ -319,6 +362,9 @@ def plotbreathwaveformwithquality(waveform, breathlist, Fs, plottype="rectangle"
319362
-------
320363
321364
"""
365+
waveform = data.data
366+
Fs = data.fs
367+
322368
# now plot the respiratory waveform, color coded for quality
323369

324370
# set up the color codes

simpleresptest

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python
22

33
import numpy as np
4-
import peakdet.io as pk_io
4+
from peakdet import Physio
55

66
import physioqc.metrics.chest_belt as chest_belt
77
import physioqc.metrics.utils as utils
@@ -11,14 +11,14 @@ samplerate, starttime, columns, data, compression, columnsource = utils.readbids
1111
colspec="respiratory_effort",
1212
)
1313

14-
rawresp = np.nan_to_num(data.flatten())
14+
rawresp = Physio(np.nan_to_num(data.flatten()), fs=samplerate)
1515

16-
print(rawresp)
16+
print(rawresp.data)
1717

18-
breathlist = chest_belt.respiratorysqi(rawresp, samplerate, debug=True)
18+
breathlist = chest_belt.respiratorysqi(rawresp, debug=True)
1919
chest_belt.plotbreathqualities(breathlist, totaltime=(len(rawresp) / samplerate))
20-
chest_belt.plotbreathwaveformwithquality(rawresp, breathlist, samplerate)
20+
chest_belt.plotbreathwaveformwithquality(rawresp, breathlist)
2121
chest_belt.plotbreathwaveformwithquality(
22-
rawresp, breathlist, samplerate, plottype="other"
22+
rawresp, breathlist, plottype="other"
2323
)
2424
chest_belt.summarizebreaths(breathlist)

0 commit comments

Comments
 (0)