Source code for vitalDSP.signal_quality_assessment.signal_quality

"""
Signal Quality Assessment Module for Physiological Signal Processing

This module provides comprehensive signal quality assessment capabilities for
physiological signals including ECG, PPG, and other vital signs. It implements
various quality metrics such as Signal-to-Noise Ratio (SNR), Peak Signal-to-Noise
Ratio (PSNR), and Mean Square Error (MSE) to evaluate signal quality and the
impact of noise or processing artifacts.

Author: vitalDSP Team
Date: 2025-01-27
Version: 1.0.0

Key Features:
- Signal-to-Noise Ratio (SNR) computation
- Peak Signal-to-Noise Ratio (PSNR) calculation
- Mean Square Error (MSE) assessment
- Noise-based SNR estimation
- Comprehensive quality metrics for signal evaluation

Examples:
--------
Basic signal quality assessment:
    >>> import numpy as np
    >>> from vitalDSP.signal_quality_assessment.signal_quality import SignalQuality
    >>> original_signal = np.sin(np.linspace(0, 10, 1000))
    >>> noise = np.random.normal(0, 0.1, 1000)
    >>> noisy_signal = original_signal + noise
    >>> sq = SignalQuality(original_signal, noisy_signal)
    >>> print(f"SNR: {sq.snr():.2f} dB")
    >>> print(f"PSNR: {sq.psnr():.2f} dB")
    >>> print(f"MSE: {sq.mse():.6f}")

Quality assessment with different noise levels:
    >>> high_noise = np.random.normal(0, 0.5, 1000)
    >>> very_noisy_signal = original_signal + high_noise
    >>> sq_high_noise = SignalQuality(original_signal, very_noisy_signal)
    >>> print(f"High noise SNR: {sq_high_noise.snr():.2f} dB")

Using noise signal directly:
    >>> sq_noise = SignalQuality(original_signal)
    >>> snr_from_noise = sq_noise.snr_of_noise(noise)
    >>> print(f"SNR from noise: {snr_from_noise:.2f} dB")
"""

import numpy as np


[docs] class SignalQuality: """ A class to assess the quality of signals using various metrics. This class provides methods to compute commonly used signal quality metrics such as Signal-to-Noise Ratio (SNR), Peak Signal-to-Noise Ratio (PSNR), and Mean Square Error (MSE). It can be used to evaluate the impact of noise or other processing on the original signal. Methods ------- snr : function Computes the Signal-to-Noise Ratio. psnr : function Computes the Peak Signal-to-Noise Ratio. mse : function Computes the Mean Square Error between the original and processed signals. snr_of_noise : function Computes the Signal-to-Noise Ratio given a noise signal. Examples -------- >>> import numpy as np >>> from vitalDSP.signal_quality_assessment.signal_quality import SignalQuality >>> >>> # Example 1: Basic signal quality assessment >>> original_signal = np.sin(np.linspace(0, 10, 1000)) >>> noise = np.random.normal(0, 0.1, 1000) >>> noisy_signal = original_signal + noise >>> sq = SignalQuality(original_signal, noisy_signal) >>> print(f"SNR: {sq.snr():.2f} dB") >>> print(f"PSNR: {sq.psnr():.2f} dB") >>> print(f"MSE: {sq.mse():.6f}") >>> >>> # Example 2: Quality assessment with different noise levels >>> high_noise = np.random.normal(0, 0.5, 1000) >>> very_noisy_signal = original_signal + high_noise >>> sq_high_noise = SignalQuality(original_signal, very_noisy_signal) >>> print(f"High noise SNR: {sq_high_noise.snr():.2f} dB") >>> >>> # Example 3: Using noise signal directly >>> sq_noise = SignalQuality(original_signal) >>> snr_from_noise = sq_noise.snr_of_noise(noise) >>> print(f"SNR from noise: {snr_from_noise:.2f} dB") """ def __init__(self, original_signal, processed_signal=None): """ Initialize the SignalQuality class with the original and processed signals. Parameters ---------- original_signal : numpy.ndarray The original, clean signal. processed_signal : numpy.ndarray, optional The processed or noisy signal for comparison. If not provided, some methods will require an alternative signal to compare against. Notes ----- The original_signal is required for all computations, while processed_signal is optional and only needed for some metrics like SNR, PSNR, and MSE. """ if not isinstance(original_signal, np.ndarray): original_signal = np.array(original_signal) self.original_signal = original_signal if processed_signal is not None: if not isinstance(processed_signal, np.ndarray): processed_signal = np.array(processed_signal) self.processed_signal = processed_signal else: self.processed_signal = None
[docs] def snr(self): """ Compute the Signal-to-Noise Ratio (SNR) of the signal. SNR is a measure of signal quality that compares the level of the desired signal to the level of background noise. A higher SNR indicates a cleaner signal with less noise. Returns ------- snr_value : float The SNR value in decibels (dB). Raises ------ ValueError If processed_signal is not provided during initialization. Examples -------- >>> signal = np.array([1, 2, 3, 4, 5]) >>> noise = np.array([0.1, 0.1, 0.1, 0.1, 0.1]) >>> noisy_signal = signal + noise >>> sq = SignalQuality(signal, noisy_signal) >>> print(sq.snr()) 14.154543666201898 """ if self.processed_signal is None: raise ValueError("Processed signal is required to compute SNR.") signal_power = np.mean(self.original_signal**2) noise_power = np.mean((self.original_signal - self.processed_signal) ** 2) if noise_power == 0: return float('inf') snr_value = 10 * np.log10(signal_power / noise_power) return snr_value
[docs] def psnr(self): """ Compute the Peak Signal-to-Noise Ratio (PSNR) of the signal. PSNR compares the maximum possible signal power to the noise power. It is commonly used in image and signal processing to assess the quality of signal reconstruction or compression techniques. Returns ------- psnr_value : float The PSNR value in decibels (dB). Raises ------ ValueError If processed_signal is not provided during initialization. Examples -------- >>> signal = np.array([1, 2, 3, 4, 5]) >>> noise = np.array([0.1, 0.1, 0.1, 0.1, 0.1]) >>> noisy_signal = signal + noise >>> sq = SignalQuality(signal, noisy_signal) >>> print(sq.psnr()) 26.020599913279625 """ if self.processed_signal is None: raise ValueError("Processed signal is required to compute PSNR.") mse_value = np.mean((self.original_signal - self.processed_signal) ** 2) if mse_value == 0: return float('inf') max_signal = np.max(self.original_signal) psnr_value = 10 * np.log10(max_signal**2 / mse_value) return psnr_value
[docs] def mse(self): """ Compute the Mean Square Error (MSE) between the original and processed signals. MSE measures the average squared difference between the original and processed signals. It is commonly used to quantify the error introduced by noise or signal processing. Returns ------- mse_value : float The MSE value. Raises ------ ValueError If processed_signal is not provided during initialization. Examples -------- >>> signal = np.array([1, 2, 3, 4, 5]) >>> noise = np.array([0.1, 0.1, 0.1, 0.1, 0.1]) >>> noisy_signal = signal + noise >>> sq = SignalQuality(signal, noisy_signal) >>> print(sq.mse()) 0.010000000000000002 """ if self.processed_signal is None: raise ValueError("Processed signal is required to compute MSE.") mse_value = np.mean((self.original_signal - self.processed_signal) ** 2) return mse_value
[docs] def snr_of_noise(self, noise_signal): """ Compute the Signal-to-Noise Ratio (SNR) given a noise signal. This method calculates the SNR by comparing the power of the original signal to the power of the provided noise signal, without needing a processed signal. Parameters ---------- noise_signal : numpy.ndarray The noise signal. Returns ------- snr_value : float The SNR value in decibels (dB). Examples -------- >>> signal = np.array([1, 2, 3, 4, 5]) >>> noise = np.array([0.1, 0.1, 0.1, 0.1, 0.1]) >>> sq = SignalQuality(signal) >>> print(sq.snr_of_noise(noise)) 20.0 """ if not isinstance(noise_signal, np.ndarray): noise_signal = np.array(noise_signal) signal_power = np.mean(self.original_signal**2) noise_power = np.mean(noise_signal**2) if noise_power == 0: return float('inf') snr_value = 10 * np.log10(signal_power / noise_power) return snr_value