Process an EMG recording#

In this tutorial, we process one EMG recording of the respiratory muscles. We load the file, remove the heart signal, find the breaths and look at the results per breath. At the end, you have the figure below.

You need M3Resp with the EMG integration (python -m pip install -e ".[emg]").

Load the recording#

See The session for what a session holds.

We make an empty session and load the EMG file into it. The file is a .Poly5 file.

import os

from m3resp import M3Session

# The example recordings are in the tests/data folder of the M3Resp source code.
data_dir = os.environ.get("M3RESP_EXAMPLE_DATA", os.path.join("tests", "data"))

session = M3Session()
emg_file = os.path.join(data_dir, "m3resp_multimodal_1_emg.Poly5")
recording = session.load_emg(emg_file)

metadata = recording["metadata"]
print("Sampling rate:", metadata["fs"], "Hz")
print("Channels:", metadata["labels"])
print("Units:", metadata["units"])
Detected .poly5
Loading .Poly5 ...
Reading file  /home/runner/work/m3resp/m3resp/tests/data/m3resp_multimodal_1_emg.Poly5
         Number of samples:  122880
         Number of channels:  2
         Sample rate: 2048 Hz
Done reading data.
Loaded .Poly5, extracting data ...
Loading data completed
Selected channels: [0, 1]
Sampling rate: 2048 Hz
Channels: ['emg_0', 'emg_1']
Units: ['uV', 'uV']

The file has two channels. Channel 1 has the EMG of the diaphragm, so we use channel 1. The channel numbers start at 0.

Process the EMG#

See Signals for how M3Resp stores a signal.

The "emg" preset runs all processing steps in the correct order:

  1. A band-pass filter from 20 Hz to 500 Hz.

  2. Detection of the R peaks of the ECG in the EMG channel.

  3. ECG gating: M3Resp removes a short part of the signal at each R peak and fills the gap.

  4. The RMS envelope of the gated signal, with a window of 0.25 s.

  5. The baseline of the envelope, with a window of 7.5 s that moves in steps of 0.2 s.

  6. Detection of the breaths in the envelope, above the baseline.

  7. The calculation of the parameters per breath and the quality checks.

session.run_preset("emg", config={"preprocess": {"channel": 1}})

for signal in session.signals:
    print(f"{signal.name}: {signal.sample_frequency} Hz")
raw_channel: 2048.0 Hz
filtered: 2048.0 Hz
envelope: 2048.0 Hz
emg_1_ecg_gated: 2048.0 Hz
emg_1_envelope: 2048.0 Hz
emg_1_moving_baseline: 2048.0 Hz

The session now has four signals of channel 1: the raw signal, the band-pass filtered signal, the envelope and the signal after ECG gating.

Look at the breaths#

See Events, intervals and breaths for what a breath holds.

The preset stores the breaths in session.events["emg_breaths"] and the R peaks of the ECG in session.events["ecg_peaks"].

breaths = session.events["emg_breaths"]
print("Number of breaths:", len(breaths))
print("Number of R peaks:", len(session.events["ecg_peaks"]))
extremum_times = [round(breath.extremum_time, 2) for breath in breaths[:5]]
print("Extremum times of the first five breaths (s):", extremum_times)
Number of breaths: 14
Number of R peaks: 28
Extremum times of the first five breaths (s): [3.01, 6.8, 11.4, 15.71, 20.08]

Each EMG breath is at the highest point of the envelope (the extremum). The default EMG detection finds only this point, so the start time and the end time of each EMG breath are equal to the extremum time.

The figure shows each step of the processing. The red lines mark the breaths.

from m3resp.visualization import plot_session_overview

fig = plot_session_overview(session, backend="plotly")
fig.show()

Look at the parameters#

See Parameters for what a parameter holds.

The last step of the preset calculates the parameters. Some parameters have one value for the recording. Other parameters have one value per breath.

rate = session.parameter_results.for_name("respiratory_rate")[0]
print(f"Respiratory rate: {rate.value:.1f} {rate.unit}")

amplitude = session.parameter_results.for_name("amplitude")[0]
first_values = [round(float(value), 2) for value in amplitude.value[:3]]
print("Amplitude of the first three breaths:", first_values)
Respiratory rate: 13.9 breaths/min
Amplitude of the first three breaths: [3.87, 3.67, 3.94]

Look at the quality checks#

See Quality flags for what a quality flag holds.

The preset also runs quality checks. Each check gives a quality flag that passed or failed.

for flag in session.quality:
    print(f"{'passed' if flag.passed else 'failed'}: {flag.name}")
failed: snr_pseudo
failed: percentage_under_baseline
passed: detect_local_high_aub
passed: detect_extreme_time_products
failed: evaluate_bell_curve_error
failed: event_detection.find_occluded_breaths
failed: event_detection.detect_ventilator_breath
failed: quality_assessment.detect_non_consecutive_manoeuvres
failed: quality_assessment.ventilator_respiratory_rate
failed: quality_assessment.evaluate_event_timing
failed: quality_assessment.evaluate_respiratory_rates
failed: quality_assessment.pocc_quality
failed: quality_assessment.interpeak_dist

Some checks need ventilator data. This recording has no ventilator data, so these checks fail. The message of each flag gives the reason.

event_detection.find_occluded_breaths: Needs ventilator pressure and PEEP inputs.
event_detection.detect_ventilator_breath: Needs ventilator volume input.
quality_assessment.detect_non_consecutive_manoeuvres: Needs ventilator breath and manoeuvre indices.
quality_assessment.ventilator_respiratory_rate: Needs ventilator volume input.
quality_assessment.evaluate_event_timing: Needs ventilator breath timing.
quality_assessment.evaluate_respiratory_rates: Needs ventilator respiratory rate (requires 'evaluate_respiratory_rates' selected together with at least two ventilator breaths).
quality_assessment.pocc_quality: Needs Pocc ventilator inputs.
quality_assessment.interpeak_dist: Needs ECG peak indices.

Recap#

In this tutorial, we:

  1. loaded an EMG recording with load_emg(),

  2. ran the "emg" preset on channel 1 with run_preset(): filter, ECG removal, envelope, baseline, breath detection and parameters,

  3. looked at the breaths, the parameters and the quality flags.

To save the results to files, use export_summary(), as in the EIT tutorial.

Next#

Total running time of the script: (0 minutes 4.402 seconds)