From 77b4ac7ea8ef58b4d6b6311215ff8a0ad69c1e2a Mon Sep 17 00:00:00 2001 From: WEN Hao Date: Thu, 16 Jan 2025 23:14:24 +0800 Subject: [PATCH 1/3] copy ricker wavelet from scipy which removed it from version 1.15.0, fixing issue #525 --- wfdb/processing/qrs.py | 53 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/wfdb/processing/qrs.py b/wfdb/processing/qrs.py index 2f0c6961..43d019cd 100644 --- a/wfdb/processing/qrs.py +++ b/wfdb/processing/qrs.py @@ -215,7 +215,7 @@ def _mwi(self): N/A """ - wavelet_filter = signal.ricker(self.qrs_width, 4) + wavelet_filter = ricker(self.qrs_width, 4) self.sig_i = ( signal.filtfilt(wavelet_filter, [1], self.sig_f, axis=0) ** 2 @@ -277,7 +277,7 @@ def _learn_init_params(self, n_calib_beats=8): qrs_amps = [] noise_amps = [] - ricker_wavelet = signal.ricker(self.qrs_radius * 2, 4).reshape(-1, 1) + ricker_wavelet = ricker(self.qrs_radius * 2, 4).reshape(-1, 1) # Find the local peaks of the signal. peak_inds_f = find_local_peaks(self.sig_f, self.qrs_radius) @@ -1776,3 +1776,52 @@ def gqrs_detect( annotations = gqrs.detect(x=d_sig, conf=conf, adc_zero=adc_zero) return np.array([a.time for a in annotations]) + + +def ricker(points, a): + """ + Return a Ricker wavelet, also known as the "Mexican hat wavelet". + + It models the function: + + ``A * (1 - (x/a)**2) * exp(-0.5*(x/a)**2)``, + + where ``A = 2/(sqrt(3*a)*(pi**0.25))``. + + This function is copied from the `scipy` library which + removed it from version 1.15.0. + + Parameters + ---------- + points : int + Number of points in `vector`. + Will be centered around 0. + a : scalar + Width parameter of the wavelet. + + Returns + ------- + vector : (N,) ndarray + Array of length `points` in shape of ricker curve. + + Examples + -------- + >>> import matplotlib.pyplot as plt + + >>> points = 100 + >>> a = 4.0 + >>> vec2 = ricker(points, a) + >>> print(len(vec2)) + 100 + >>> plt.plot(vec2) + >>> plt.show() + + """ + A = 2 / (np.sqrt(3 * a) * (np.pi**0.25)) + wsq = a**2 + vec = np.arange(0, points) - (points - 1.0) / 2 + xsq = vec**2 + mod = (1 - xsq / wsq) + gauss = np.exp(-xsq / (2 * wsq)) + total = A * mod * gauss + return total From 0ee54ef375b27643d2e003e4d904258d829a9521 Mon Sep 17 00:00:00 2001 From: WEN Hao Date: Sat, 18 Jan 2025 22:14:45 +0800 Subject: [PATCH 2/3] run black --- wfdb/processing/qrs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wfdb/processing/qrs.py b/wfdb/processing/qrs.py index 43d019cd..b4f64172 100644 --- a/wfdb/processing/qrs.py +++ b/wfdb/processing/qrs.py @@ -1821,7 +1821,7 @@ def ricker(points, a): wsq = a**2 vec = np.arange(0, points) - (points - 1.0) / 2 xsq = vec**2 - mod = (1 - xsq / wsq) + mod = 1 - xsq / wsq gauss = np.exp(-xsq / (2 * wsq)) total = A * mod * gauss return total From dd6506f1254b0ef24d200529f8b76cf1c943def6 Mon Sep 17 00:00:00 2001 From: WEN Hao Date: Sat, 18 Jan 2025 22:28:15 +0800 Subject: [PATCH 3/3] add scipy license for the ricker function --- wfdb/processing/qrs.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/wfdb/processing/qrs.py b/wfdb/processing/qrs.py index b4f64172..6f2f4bc4 100644 --- a/wfdb/processing/qrs.py +++ b/wfdb/processing/qrs.py @@ -1778,6 +1778,43 @@ def gqrs_detect( return np.array([a.time for a in annotations]) +# This function includes code from SciPy, which is licensed under the +# BSD 3-Clause "New" or "Revised" License. +# The original code can be found at: +# https://github.com/scipy/scipy/blob/v1.14.0/scipy/signal/_wavelets.py#L316-L359 + +# Copyright (c) 2001-2002 Enthought, Inc. 2003, SciPy Developers. +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. + +# 2. Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. + +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + def ricker(points, a): """ Return a Ricker wavelet, also known as the "Mexican hat wavelet".