PySDKit 0.4__py3-none-any.whl → 0.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PySDKit
3
- Version: 0.4
3
+ Version: 0.4.2
4
4
  Summary: A Python library for signal decomposition algorithms with a unified interface
5
5
  Home-page: https://github.com/wwhenxuan/PySDKit
6
6
  Author: whenxuan
@@ -31,9 +31,7 @@ Requires-Dist: matplotlib (>=3.7.2)
31
31
 
32
32
  A Python library for signal decomposition algorithms
33
33
 
34
- <div align="center">
35
- <img src=".\images\Logo_sd.png" alt="Logo_sd" width="500"/>
36
- </div>
34
+ ![Logo](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/Logo_sd.png)
37
35
 
38
36
  ## Installation
39
37
 
@@ -67,7 +65,7 @@ IMFs = emd.fit_transform(signal, max_imf=2)
67
65
  plot_IMFs(signal, IMFs)
68
66
  ~~~
69
67
 
70
- <img src=".\images\example.jpg" alt="example" />
68
+ ![example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/example.jpg)
71
69
 
72
70
  The EMD in the above example is the most classic `empirical mode decomposition` algorithm in signal decomposition. For more complex signals, you can try other algorithms such as variational mode decomposition ([`VMD`](https://ieeexplore.ieee.org/abstract/document/6655981)).
73
71
 
@@ -85,7 +83,7 @@ print(IMFs.shape)
85
83
  vmd.plot_IMFs(save_figure=True)
86
84
  ~~~
87
85
 
88
- <img src=".\images\vmd_example.jpg" alt="vmd_example.jpg" />
86
+ ![vmd_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/vmd_example.jpg)
89
87
 
90
88
  Better observe the characteristics of the decomposed intrinsic mode function in the frequency domain.
91
89
 
@@ -96,5 +94,4 @@ from pysdkit.plot import plot_IMFs_amplitude_spectra
96
94
  plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
97
95
  ~~~
98
96
 
99
- <img src=".\images\frequency_example.jpg" alt="frequency_example" />
100
-
97
+ ![frequency_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/frequency_example.jpg)
@@ -1,16 +1,16 @@
1
- pysdkit/__init__.py,sha256=nsuYXOdFNc3J-NNu5kkMXbeF8z_CBVLIo3tTKwEP53k,259
1
+ pysdkit/__init__.py,sha256=dHeg5ai9k1iznEr0OdtVkKTQLmeOOTsDa9AynzOOPXA,262
2
2
  pysdkit/data/__init__.py,sha256=i_ZMKR_FcLq8wWXJ0MyLA4S41iXTMWbpf9sGpoLWdpw,621
3
3
  pysdkit/data/_add_noise.py,sha256=8cGzAquIPlf0hzTMUOBj8R1SEjGd2-JveWyzPPII05M,949
4
4
  pysdkit/data/_generator.py,sha256=gW7D9A8OTKJ5CBQ40oTbowu9PMZx-WiyN43oz42OAL4,12119
5
5
  pysdkit/emd/BEMD.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- pysdkit/emd/EMD.py,sha256=xyQT7IqitAQ9mdOfDDh4QEwlFU7SODl_9y5wd3M-v-k,18090
6
+ pysdkit/emd/EMD.py,sha256=dvhfOT_qfZnKJywQpSQSsiCNcMYF7hC9Pau4GJezcL8,18092
7
7
  pysdkit/emd/__init__.py,sha256=HdOfha6kuiKz2sdTENF_7bHTN2BT2Qtr5i1xXc9dOm8,22
8
8
  pysdkit/emd/_find_extrema.py,sha256=EnUZB9Z4uUaVTfuH2J2hERUR6cmsWQ890gQt8BpE1f0,5368
9
9
  pysdkit/emd/_prepare_points.py,sha256=XwEkwRPmHTZZcHhjiBx2lv_Qvj-gx2lw76locyOBf-8,10528
10
10
  pysdkit/emd/_splines.py,sha256=aaAULcznyn6mSciDeht4hdKZx1MhvKTvd0Z-bQVpd9E,2575
11
11
  pysdkit/emd2d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- pysdkit/ewt/__init__.py,sha256=Yz5QcfLHTqf_69qJ663ESoUzrbKSnhHY0angsegqhvk,60
13
- pysdkit/ewt/_ewt.py,sha256=TVo7GbVtBtFr7vHFQmE2PPyxX3hp0Pe3aNQyNAH_P5E,15703
12
+ pysdkit/ewt/__init__.py,sha256=DPk4dSLaTyelSoqZ4xCSWpGA1R3jSoNrzP-oIJG_MTQ,42
13
+ pysdkit/ewt/ewt.py,sha256=dNPp7-nEqSZS1ZAk6ul7SDjWqo33hrh1OAvA-iYZOPg,21330
14
14
  pysdkit/metrics/_Index_of_Orthogonality.py,sha256=nmUEP56TVeZqKogP0QqylJwLL3QMxUNLup273PM1yV4,1128
15
15
  pysdkit/metrics/__init__.py,sha256=4TgRjJuOxNMCEloP-Vnq-z5VnrzYKQCypUaAMJb_nkI,169
16
16
  pysdkit/plot/__init__.py,sha256=_SklnKYnFm42Yso9R_UGkzwyyi98tTngkHhgQ4JrIug,92
@@ -18,7 +18,7 @@ pysdkit/plot/_fourier_spectra.py,sha256=7HzjHf6EeZ2DScgvMvveIQYSPVhXLfQSL6sGSUJm
18
18
  pysdkit/plot/_functions.py,sha256=rakosRbEg6KgdQJB26x6DGO6On3Ht9KD2MR2Wu7gSyc,4040
19
19
  pysdkit/plot/_plot_imfs.py,sha256=YsVyazrSwBzQ3EmLj7pzZqVmxolTbSljYnAP-sIJRdQ,4239
20
20
  pysdkit/plot/main.py,sha256=O_CKGAPPVaNBOqZwA7FrWcGJJEz76bbL_PomNJMtO7w,102
21
- pysdkit/utils/__init__.py,sha256=6M-DhV8ghK_O2PoMBnj6tLbv_3XcR1CeAqxw-DM0Ass,1076
21
+ pysdkit/utils/__init__.py,sha256=1EyfVWKV6cMXPapt9lYE0pHlgAaxAhphNEo56K8GVz0,1759
22
22
  pysdkit/utils/_differ.py,sha256=q0AoYecFLOlPsFcbjV7G_r4AGfp8WfVJ3aD9rbHIdJo,866
23
23
  pysdkit/utils/_fft.py,sha256=VgluJaLeuuJ333_kJH1CKDbnlc6m_KX60pNfdRbv73Q,472
24
24
  pysdkit/utils/_function.py,sha256=dBF4gSjEBavAZjx0PrnZ8Z_iBk5T-zLmU3XEC1XvwTE,1436
@@ -30,13 +30,13 @@ pysdkit/vmd/__init__.py,sha256=nNmNpo65_SlqZhqQXsYj-FRkCO-KHSRY2XeGp5vmzPo,73
30
30
  pysdkit/vmd/_avmd.py,sha256=xUtmiyFOqkol7Oqh_yDPzGAr6b7FrfVXG6AlDOnTvr8,473
31
31
  pysdkit/vmd/_mvmd.py,sha256=ANXqmfJA0Y5Ib80Yw0URzqHIIFFiJfg5p1CswTXiM1o,9109
32
32
  pysdkit/vmd/_svmd.py,sha256=JDoR0BrocbsHn-zeg2fJ4wkuykEVh700yZKdgPk5M2o,384
33
- pysdkit/vmd/_vmd_c.py,sha256=BxgdERwbyEE88NWvaKCuhSBxB3gz9hUr-2qWDoRpfZU,8758
34
- pysdkit/vmd/_vmd_f.py,sha256=kb2GiAThe_xqU7qLATkOdlbXj4kWFFLayKJ1nouc6eQ,5402
33
+ pysdkit/vmd/_vmd_c.py,sha256=gLhUC4PTHm2pPQI6l-Plumzm1flUJ_sYhgx79MOAEeY,8756
34
+ pysdkit/vmd/_vmd_f.py,sha256=S0ucWz_KDsiXwmRlt6lVPRBOurXOE05WSOgggT0lqP0,5390
35
35
  pysdkit/vmd/base.py,sha256=frcDhMadR2WHM6UTnjGgufAiZhweqrNti9E71h7zUQI,3442
36
36
  pysdkit/vncmd/__init__.py,sha256=n9grWZ12fpbtfAtiQO50Bkg1sB0eiUDXbiMmbWk_i8c,25
37
37
  pysdkit/vncmd/vncmd.py,sha256=digpTAUGRsddbS3bM-9p6-5tENq-__AT7aonYgVHEzA,12362
38
- PySDKit-0.4.dist-info/LICENSE,sha256=ogEPNDSH0_dhiv_lT3ifVIdgIzHAqNA_SemnxUfPBJk,7048
39
- PySDKit-0.4.dist-info/METADATA,sha256=of4rNNif_JkVzmWHNyit6nXJjnqvKGTV4anDdQ9HOFw,4268
40
- PySDKit-0.4.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
41
- PySDKit-0.4.dist-info/top_level.txt,sha256=wN_Yil0woB5Tu_WrIRt4_T4eoRyXljCYvH0dL6-7ei0,8
42
- PySDKit-0.4.dist-info/RECORD,,
38
+ PySDKit-0.4.2.dist-info/LICENSE,sha256=ogEPNDSH0_dhiv_lT3ifVIdgIzHAqNA_SemnxUfPBJk,7048
39
+ PySDKit-0.4.2.dist-info/METADATA,sha256=Ea1kcbITEGZVAdixzCTO1ZUsCz1Sej_yaoOCi2Te1fQ,4376
40
+ PySDKit-0.4.2.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
41
+ PySDKit-0.4.2.dist-info/top_level.txt,sha256=wN_Yil0woB5Tu_WrIRt4_T4eoRyXljCYvH0dL6-7ei0,8
42
+ PySDKit-0.4.2.dist-info/RECORD,,
pysdkit/__init__.py CHANGED
@@ -11,4 +11,4 @@ from .vmd import MVMD
11
11
  from .vncmd import VNCMD
12
12
 
13
13
  # 经验小波变换
14
- from .ewt import EWT1D
14
+ from .ewt import ewt, EWT
pysdkit/emd/EMD.py CHANGED
@@ -449,8 +449,9 @@ class EMD(object):
449
449
 
450
450
  if __name__ == '__main__':
451
451
  from matplotlib import pyplot as plt
452
+
452
453
  x = np.linspace(0, 1, 1024)
453
454
  f = np.cos(22 * np.pi * x ** 2) + 6 * x ** 2
454
455
  emd = EMD(spline_kind="akima")
455
456
  IMFs = emd.fit_transform(f, max_imf=2)
456
- print(IMFs.shape)
457
+ print(IMFs.shape)
pysdkit/ewt/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
- from ._ewt import EWT1D
2
- from ._ewt import EWT_Meyer_Wavelet
1
+ from .ewt import ewt
2
+ from .ewt import EWT
pysdkit/ewt/ewt.py ADDED
@@ -0,0 +1,500 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on 2024/7/12 13:41
4
+ @author: Whenxuan Wang
5
+ @email: wwhenxuan@gmail.com
6
+ Empirical Wavelet Transform for 1D signals
7
+ Original paper:
8
+ Gilles, J., 2013. Empirical Wavelet Transform. IEEE Transactions on Signal Processing, 61(16), pp.3999–4010.
9
+ Available at: http://ieeexplore.ieee.org/lpdocs/epic03/wrapper.htm?arnumber=6522142.
10
+ Original Matlab toolbox: https://www.mathworks.com/matlabcentral/fileexchange/42141-empirical-wavelet-transforms
11
+ Original Code from: https://github.com/vrcarva/ewtpy
12
+ """
13
+ import numpy as np
14
+ from scipy.ndimage.filters import gaussian_filter
15
+ from typing import Optional
16
+
17
+
18
+ class EWT(object):
19
+ """Empirical Wavelet Transform with Class Interface."""
20
+
21
+ def __init__(self,
22
+ N: Optional[int] = 5,
23
+ log: Optional[float] = 0,
24
+ detect: Optional[str] = "locmax",
25
+ completion: Optional[float] = 0,
26
+ reg: Optional[str] = 'average',
27
+ lengthFilter: Optional[float] = 10,
28
+ sigmaFilter: Optional[float] = 5) -> None:
29
+ """
30
+ :param N: Maximum number of modes (signal components) to detect and extract.
31
+ :param log: Set to 0 or 1 to indicate whether to operate in the logarithmic spectrum.
32
+ :param detect: Method for detecting boundaries in the Fourier domain ('locmax' or other detection methods).
33
+ :param completion: Set to 0 or 1 to indicate whether to complete the number of modes to N if fewer are detected.
34
+ :param reg: Regularization method applied to the filter bank ('none', 'gaussian', or 'average').
35
+ :param lengthFilter: Width of the filters used in regularization (for Gaussian or average filters).
36
+ :param sigmaFilter: Standard deviation for the Gaussian filter in the regularization step.
37
+ """
38
+ self.N = N
39
+ self.log = log
40
+ self.detect = detect
41
+ self.completion = completion
42
+ self.reg = reg
43
+ self.lengthFilter = lengthFilter
44
+ self.sigmaFilter = sigmaFilter
45
+
46
+ def fit_transform(self, signal: np.ndarray, N: Optional[int] = None, return_all: Optional[bool] = False):
47
+ """
48
+ Perform Empirical Wavelet Transform on the input signal.
49
+
50
+ :param signal: Input signal array to be decomposed.
51
+ :param N: Number of modes to extract. Defaults to the value specified during initialization.
52
+ :param return_all: If True, return the EWT decomposition, the filter bank, and the boundaries.
53
+ If False, return only the EWT decomposition.
54
+
55
+ :returns:
56
+ - ewt: The extracted modes from the signal.
57
+ - mfb: The filter bank applied in the Fourier domain (only if return_all is True).
58
+ - boundaries: Boundaries detected in the Fourier spectrum (only if return_all is True).
59
+ """
60
+ N = self.N if N is None else N
61
+ # Compute the one-sided magnitude of the signal's Fourier transform
62
+ signal_ff = np.fft.fft(signal)
63
+ signal_ff = abs(signal_ff[0:int(np.ceil(signal_ff.size / 2))]) # one-sided magnitude
64
+
65
+ # Detect boundaries in the Fourier domain
66
+ boundaries = EWT_Boundaries_Detect(signal_ff, self.log, self.detect, N, self.reg,
67
+ self.lengthFilter, self.sigmaFilter)
68
+ boundaries = boundaries * np.pi / round(signal_ff.size)
69
+
70
+ # If fewer boundaries are detected, complete to reach N-1 if completion is enabled
71
+ if self.completion == 1 and len(boundaries) < N - 1:
72
+ boundaries = EWT_Boundaries_Completion(boundaries, N - 1)
73
+
74
+ # Extend the signal by mirroring to avoid boundary effects during filtering
75
+ ltemp = int(np.ceil(signal.size / 2))
76
+ fMirr = fmirror(ts=signal, sym=ltemp, end=1)
77
+ ffMirr = np.fft.fft(fMirr)
78
+
79
+ # Build the filter bank based on the detected boundaries
80
+ mfb = EWT_Meyer_FilterBank(boundaries, ffMirr.size)
81
+
82
+ # Filter the signal to extract each subband
83
+ ewt = np.zeros(mfb.shape)
84
+ for k in range(mfb.shape[1]):
85
+ ewt[:, k] = np.real(np.fft.ifft(np.conjugate(mfb[:, k]) * ffMirr))
86
+ ewt = ewt[ltemp - 1:-ltemp, :].T
87
+
88
+ # Return the requested data
89
+ if return_all is True:
90
+ return ewt, mfb, boundaries
91
+ else:
92
+ return ewt
93
+
94
+
95
+ def ewt(signal, N: Optional[int] = 5,
96
+ log: Optional[float] = 0,
97
+ detect: Optional[str] = "locmax",
98
+ completion: Optional[float] = 0,
99
+ reg: Optional[str] = 'average',
100
+ lengthFilter: Optional[float] = 10,
101
+ sigmaFilter: Optional[float] = 5,
102
+ return_all: Optional[bool] = False):
103
+ """
104
+ Empirical Wavelet Transform with Function Interface.
105
+
106
+ :param signal: The input signal to be decomposed.
107
+ :param N: Maximum number of modes (signal components) to detect and extract.
108
+ :param log: Set to 0 or 1 to indicate whether to work with the logarithmic spectrum.
109
+ :param detect: Method for detecting boundaries in the Fourier domain ('locmax' or other methods).
110
+ :param completion: Set to 0 or 1 to indicate whether to complete the number of modes to N if fewer are detected.
111
+ :param reg: Regularization method applied to the filter bank ('none', 'gaussian', or 'average').
112
+ :param lengthFilter: Width of the filters used in regularization (for Gaussian or average filters).
113
+ :param sigmaFilter: Standard deviation for the Gaussian filter in the regularization step.
114
+ :param return_all: If True, return the EWT decomposition, the filter bank, and the boundaries.
115
+ If False, return only the EWT decomposition.
116
+
117
+ :returns:
118
+ - ewt: The extracted modes from the signal.
119
+ - mfb: The filter bank in the Fourier domain (only if return_all is True).
120
+ - boundaries: Boundaries detected in the Fourier spectrum (only if return_all is True).
121
+ """
122
+ # Compute the one-sided magnitude of the signal's Fourier transform
123
+ ff = np.fft.fft(signal)
124
+ ff = abs(ff[0:int(np.ceil(ff.size / 2))]) # one-sided magnitude
125
+
126
+ # Detect boundaries in the Fourier domain
127
+ boundaries = EWT_Boundaries_Detect(ff, log, detect, N, reg, lengthFilter, sigmaFilter)
128
+ boundaries = boundaries * np.pi / round(ff.size)
129
+
130
+ # If fewer boundaries are detected, complete to reach N-1 if completion is enabled
131
+ if completion == 1 and len(boundaries) < N - 1:
132
+ boundaries = EWT_Boundaries_Completion(boundaries, N - 1)
133
+
134
+ # Extend the signal by mirroring to avoid boundary effects during filtering
135
+ ltemp = int(np.ceil(signal.size / 2)) # Similar to MATLAB's round function
136
+ fMirr = fmirror(ts=signal, sym=ltemp, end=1)
137
+ ffMirr = np.fft.fft(fMirr)
138
+
139
+ # Build the filter bank based on the detected boundaries
140
+ mfb = EWT_Meyer_FilterBank(boundaries, ffMirr.size)
141
+
142
+ # Filter the signal to extract each subband
143
+ ewt = np.zeros(mfb.shape)
144
+ for k in range(mfb.shape[1]):
145
+ ewt[:, k] = np.real(np.fft.ifft(np.conjugate(mfb[:, k]) * ffMirr))
146
+ ewt = ewt[ltemp - 1:-ltemp, :].T
147
+
148
+ # Return the requested data
149
+ if return_all is True:
150
+ return ewt, mfb, boundaries
151
+ else:
152
+ return ewt
153
+
154
+
155
+ def fmirror(ts: np.ndarray, sym: int, end: int):
156
+ """Implements a signal mirroring expansion function."""
157
+ fMirr = np.append(np.flip(ts[0:sym - end], axis=0), ts)
158
+ fMirr = np.append(fMirr, np.flip(ts[-sym - end:-end], axis=0))
159
+ return fMirr
160
+
161
+
162
+ def EWT_Boundaries_Detect(ff, log, detect, N, reg, lengthFilter, sigmaFilter):
163
+ """
164
+ Segments the input function `ff` into a certain number of supports (frequency bands)
165
+ using different techniques for boundary detection and regularization.
166
+
167
+ :param ff: The function (signal) to segment (typically in the frequency domain).
168
+ :param log: 0 or 1, indicating whether to apply logarithmic transformation to the spectrum.
169
+ :param detect: The method used for boundary detection. Options include:
170
+ - 'locmax': Mid-point between consecutive local maxima (default).
171
+ - 'locmaxmin': Lowest minima between consecutive local maxima.
172
+ - 'locmaxminf': Lowest minima between consecutive local maxima of the original spectrum.
173
+ :param N: Maximum number of supports (modes or signal components) to detect.
174
+ :param reg: The regularization method to apply to the spectrum. Options include:
175
+ - 'none': No regularization.
176
+ - 'gaussian': Gaussian filtering with width `lengthFilter` and standard deviation `sigmaFilter`.
177
+ - 'average': Average filtering with width `lengthFilter`.
178
+ :param lengthFilter: The width of the Gaussian or average filter for regularization.
179
+ :param sigmaFilter: The standard deviation of the Gaussian filter for regularization.
180
+
181
+ :returns:
182
+ - boundaries: A list of detected boundaries (in terms of indices).
183
+
184
+ This function detects boundaries in the frequency domain based on different detection
185
+ methods. The spectrum can be regularized using Gaussian or average filters, which smooths
186
+ out the spectrum before detection.
187
+
188
+ - The 'locmax' method detects the mid-points between consecutive local maxima.
189
+ - The 'locmaxmin' method extracts the lowest minima between consecutive maxima.
190
+ - The 'locmaxminf' method uses the regularized spectrum for maxima detection and the original spectrum for minima.
191
+
192
+ """
193
+ # Apply log transformation if needed
194
+ if log == 1:
195
+ ff = np.log(ff)
196
+
197
+ # Apply regularization if needed
198
+ if reg == 'average':
199
+ regFilter = np.ones(lengthFilter) / lengthFilter
200
+ presig = np.convolve(ff, regFilter, mode='same') # Averaging filter
201
+
202
+ elif reg == 'gaussian':
203
+ regFilter = np.zeros(lengthFilter)
204
+ regFilter[regFilter.size // 2] = 1 # Ensure center is set for Gaussian filtering
205
+ presig = np.convolve(ff, gaussian_filter(regFilter, sigmaFilter), mode='same') # Gaussian filter
206
+ else:
207
+ presig = ff # No regularization applied
208
+
209
+ # Boundary detection based on the selected method
210
+ if detect == "locmax":
211
+ boundaries = LocalMax(presig, N) # Mid-point between consecutive local maxima
212
+
213
+ elif detect == "locmaxmin":
214
+ boundaries = LocalMaxMin(presig, N) # Lowest local minima between selected maxima
215
+
216
+ elif detect == "locmaxminf":
217
+ boundaries = LocalMaxMin(presig, N, fm=ff) # Minima on the original spectrum between maxima on regularized spectrum
218
+
219
+ else:
220
+ raise ValueError("Invalid detection method provided.")
221
+
222
+ return boundaries + 1 # Increment indices by 1 to account for zero-based indexing
223
+
224
+
225
+
226
+ def LocalMax(ff, N):
227
+ """
228
+ Segments the input function `ff` into a maximum of `N` bands by locating
229
+ the N largest local maxima and calculating the middle points between them.
230
+
231
+ :param ff: The input function (signal) to segment.
232
+ :param N: The maximum number of bands (supports) to detect.
233
+
234
+ :returns:
235
+ - bound: A list of indices representing the detected boundaries between bands.
236
+
237
+ This function works by identifying local maxima in the input function `ff`,
238
+ selecting the `N` largest maxima, and then computing the midpoint between each
239
+ consecutive maximum to define the segment boundaries.
240
+ """
241
+ N = N - 1
242
+ locmax = np.zeros(ff.size) # Initialize array to store local maxima
243
+ locmin = max(ff) * np.ones(ff.size) # Initialize array for local minima
244
+
245
+ # Loop through the signal to find local maxima and minima
246
+ for i in np.arange(1, ff.size - 1):
247
+ if ff[i - 1] < ff[i] and ff[i] > ff[i + 1]:
248
+ locmax[i] = ff[i] # Mark local maxima
249
+ if ff[i - 1] > ff[i] and ff[i] <= ff[i + 1]:
250
+ locmin[i] = ff[i] # Mark local minima
251
+
252
+ N = min(N, locmax.size) # Ensure N does not exceed the number of available maxima
253
+ # Find the indices of the N largest maxima
254
+ maxidxs = np.sort(locmax.argsort()[::-1][:N])
255
+
256
+ # Calculate the midpoints between consecutive maxima to define the boundaries
257
+ bound = np.zeros(N)
258
+ for i in range(N):
259
+ if i == 0:
260
+ a = 0
261
+ else:
262
+ a = maxidxs[i - 1]
263
+ bound[i] = (a + maxidxs[i]) / 2 # Calculate midpoint
264
+
265
+ return bound
266
+
267
+
268
+ def LocalMaxMin(f, N, fm=0):
269
+ """
270
+ Segments the input function `f` into a maximum of `N` bands by detecting
271
+ the lowest local minima between the `N` largest local maxima. If `fm` is provided,
272
+ local maxima are computed on `f` and local minima are computed on `fm`. Otherwise,
273
+ both maxima and minima are computed on `f`.
274
+
275
+ This is useful when you want to compute the maxima on a regularized version of
276
+ your signal (f) while detecting the "true" minima on a different version (fm).
277
+
278
+ :param f: The function (signal) to segment.
279
+ :param N: The maximum number of bands (supports) to detect.
280
+ :param fm: (Optional) The function on which local minima will be computed.
281
+ If not provided, local minima will be computed on `f`.
282
+
283
+ :returns:
284
+ - bound: A list of indices representing the detected boundaries between bands.
285
+
286
+ This function detects local maxima and minima in the signal and then calculates
287
+ boundaries based on the N largest maxima and the lowest minima between them.
288
+ """
289
+ # Initialize array to store local maxima
290
+ locmax = np.zeros(f.size)
291
+
292
+ # If fm is not provided, use f for both maxima and minima detection
293
+ if type(fm) == int:
294
+ f2 = f
295
+ else:
296
+ f2 = fm
297
+
298
+ # Initialize array for local minima
299
+ locmin = max(f2) * np.ones(f2.size)
300
+
301
+ # Detect local minima and maxima in the signal
302
+ for i in np.arange(1, f.size - 1):
303
+ if (f[i - 1] < f[i]) and (f[i] > f[i + 1]):
304
+ locmax[i] = f[i] # Mark local maxima
305
+ if (f2[i - 1] > f2[i]) and (f2[i] < f2[i + 1]):
306
+ locmin[i] = f2[i] # Mark local minima
307
+
308
+ # Initialize the boundaries array
309
+ bound = np.zeros(N)
310
+
311
+ if N != -1:
312
+ N = N - 1
313
+ # Keep the N largest maxima
314
+ Imax = np.sort(locmax.argsort()[::-1][:N])
315
+
316
+ # Detect the lowest minima between two consecutive maxima
317
+ for i in range(N):
318
+ if i == 0:
319
+ a = 1
320
+ else:
321
+ a = Imax[i - 1]
322
+
323
+ # Sort minima between the current and previous maxima
324
+ lmin = np.sort(locmin[a:Imax[i]])
325
+ ind = np.argsort(locmin[a:Imax[i]])
326
+
327
+ # Find the true minimum
328
+ tmpp = lmin[0]
329
+ n = 0
330
+ if n < len(lmin):
331
+ n = 1
332
+ while (n < len(lmin)) and (tmpp == lmin[n]):
333
+ n += 1
334
+
335
+ # Set the boundary to the midpoint of the smallest minima
336
+ bound[i] = a + ind[n // 2] - 1
337
+ else:
338
+ # Case when N is set to -1, meaning no limit on the number of bands
339
+ k = 0
340
+ for i in range(locmin.size):
341
+ if locmin[i] < max(f2):
342
+ bound[k] = i - 1
343
+ k += 1
344
+
345
+ return bound
346
+
347
+
348
+ def EWT_Boundaries_Completion(boundaries, NT):
349
+ """
350
+ Completes the boundaries vector to ensure a total of `NT` boundaries by
351
+ equally splitting the last band (highest frequencies) between the final boundary
352
+ and π.
353
+
354
+ :param boundaries: The initial boundaries vector to be completed.
355
+ :param NT: The total number of boundaries desired.
356
+
357
+ :returns:
358
+ - boundaries: The completed boundaries vector.
359
+
360
+ This function adds additional boundaries by uniformly dividing the highest frequency
361
+ band (i.e., the region from the last boundary to π) into equal parts, such that the total
362
+ number of boundaries reaches `NT`.
363
+ """
364
+ # Calculate the number of additional boundaries needed
365
+ Nd = NT - len(boundaries)
366
+
367
+ # Calculate the width of each new segment to be added
368
+ deltaw = (np.pi - boundaries[-1]) / (Nd + 1)
369
+
370
+ # Add the new boundaries to the vector
371
+ for k in range(Nd):
372
+ boundaries = np.append(boundaries, boundaries[-1] + deltaw)
373
+
374
+ return boundaries
375
+
376
+
377
+ def EWT_Meyer_FilterBank(boundaries, Nsig):
378
+ """
379
+ Generates the filter bank (scaling function + wavelets) corresponding to the
380
+ provided set of frequency segments.
381
+
382
+ :param boundaries: A vector containing the boundaries of frequency segments.
383
+ Note that 0 and π should NOT be included in this vector.
384
+ :param Nsig: The length of the input signal.
385
+
386
+ :returns:
387
+ - mfb: A matrix where each column represents a filter in the Fourier domain.
388
+ The first column corresponds to the scaling function, and the
389
+ subsequent columns represent the successive wavelets.
390
+
391
+ This function generates a set of Meyer wavelets and a scaling function in the
392
+ Fourier domain based on the provided frequency boundaries.
393
+ """
394
+ Npic = len(boundaries) # Number of frequency boundaries
395
+ # Compute gamma, which controls the transition width of the wavelets
396
+ gamma = 1
397
+ for k in range(Npic - 1):
398
+ r = (boundaries[k + 1] - boundaries[k]) / (boundaries[k + 1] + boundaries[k])
399
+ if r < gamma:
400
+ gamma = r
401
+ r = (np.pi - boundaries[Npic - 1]) / (np.pi + boundaries[Npic - 1])
402
+ if r < gamma:
403
+ gamma = r
404
+ gamma = (1 - 1 / Nsig) * gamma # Ensure gamma is strictly less than the minimum
405
+
406
+ # Initialize the filter bank matrix
407
+ mfb = np.zeros([Nsig, Npic + 1])
408
+
409
+ # EWT_Meyer_Scaling - Generate the scaling function
410
+ Mi = int(np.floor(Nsig / 2)) # Half the signal length
411
+ w = np.fft.fftshift(np.linspace(0, 2 * np.pi - 2 * np.pi / Nsig, num=Nsig)) # Frequency grid
412
+ w[0:Mi] = -2 * np.pi + w[0:Mi] # Shift the negative frequencies
413
+ aw = abs(w) # Absolute frequency values
414
+ yms = np.zeros(Nsig) # Initialize the scaling function
415
+ an = 1. / (2 * gamma * boundaries[0])
416
+ pbn = (1. + gamma) * boundaries[0]
417
+ mbn = (1. - gamma) * boundaries[0]
418
+
419
+ # Generate the scaling function based on the first boundary
420
+ for k in range(Nsig):
421
+ if aw[k] <= mbn:
422
+ yms[k] = 1
423
+ elif ((aw[k] >= mbn) and (aw[k] <= pbn)):
424
+ yms[k] = np.cos(np.pi * EWT_beta(an * (aw[k] - mbn)) / 2)
425
+
426
+ yms = np.fft.ifftshift(yms) # Shift the scaling function back
427
+ mfb[:, 0] = yms # Store the scaling function in the filter bank
428
+
429
+ # Generate the wavelets for the remaining boundaries
430
+ for k in range(Npic - 1):
431
+ mfb[:, k + 1] = EWT_Meyer_Wavelet(boundaries[k], boundaries[k + 1], gamma, Nsig)
432
+
433
+ # Generate the final wavelet for the last boundary segment (up to π)
434
+ mfb[:, Npic] = EWT_Meyer_Wavelet(boundaries[Npic - 1], np.pi, gamma, Nsig)
435
+
436
+ return mfb
437
+
438
+
439
+ def EWT_beta(x):
440
+ """
441
+ Beta = EWT_beta(x)
442
+ function used in the construction of Meyer's wavelet
443
+ """
444
+ if x < 0:
445
+ bm = 0
446
+ elif x > 1:
447
+ bm = 1
448
+ else:
449
+ bm = (x ** 4) * (35. - 84. * x + 70. * (x ** 2) - 20. * (x ** 3))
450
+ return bm
451
+
452
+
453
+ def EWT_Meyer_Wavelet(wn, wm, gamma, Nsig):
454
+ """
455
+ Generates the 1D Meyer wavelet in the Fourier domain associated with the
456
+ scale segment [wn, wm] with a transition ratio `gamma`.
457
+
458
+ :param wn: The lower boundary of the frequency segment.
459
+ :param wm: The upper boundary of the frequency segment.
460
+ :param gamma: The transition ratio that controls the width of the transition zones
461
+ between frequency bands.
462
+ :param Nsig: The number of points in the signal.
463
+
464
+ :returns:
465
+ - ymw: The Fourier transform of the wavelet for the frequency band [wn, wm].
466
+
467
+ This function creates a Meyer wavelet in the Fourier domain with smooth transitions
468
+ between frequency bands defined by `wn` and `wm`.
469
+ """
470
+ # Compute the frequency grid
471
+ Mi = int(np.floor(Nsig / 2)) # Half the signal length
472
+ w = np.fft.fftshift(np.linspace(0, 2 * np.pi - 2 * np.pi / Nsig, num=Nsig)) # Frequency grid
473
+ w[0:Mi] = -2 * np.pi + w[0:Mi] # Shift the negative frequencies
474
+ aw = abs(w) # Absolute frequency values
475
+
476
+ # Initialize the wavelet in the Fourier domain
477
+ ymw = np.zeros(Nsig)
478
+
479
+ # Parameters for the Meyer wavelet transitions
480
+ an = 1. / (2 * gamma * wn)
481
+ am = 1. / (2 * gamma * wm)
482
+ pbn = (1. + gamma) * wn
483
+ mbn = (1. - gamma) * wn
484
+ pbm = (1. + gamma) * wm
485
+ mbm = (1. - gamma) * wm
486
+
487
+ # Construct the wavelet in the Fourier domain based on the segment boundaries
488
+ for k in range(Nsig):
489
+ if (aw[k] >= pbn) and (aw[k] <= mbm): # Main passband
490
+ ymw[k] = 1
491
+ elif (aw[k] >= mbm) and (aw[k] <= pbm): # Smooth transition in upper boundary
492
+ ymw[k] = np.cos(np.pi * EWT_beta(am * (aw[k] - mbm)) / 2)
493
+ elif (aw[k] >= mbn) and (aw[k] <= pbn): # Smooth transition in lower boundary
494
+ ymw[k] = np.sin(np.pi * EWT_beta(an * (aw[k] - mbn)) / 2)
495
+
496
+ # Shift the wavelet back to the normal order
497
+ ymw = np.fft.ifftshift(ymw)
498
+
499
+ return ymw
500
+
pysdkit/utils/__init__.py CHANGED
@@ -1,34 +1,35 @@
1
- # 傅里叶变换
2
- from ._fft import fft, ifft, fftshift, ifftshift
3
-
4
- # 信号的镜像拓展
5
- from .mirror import fmirror
6
-
7
- # 希尔伯特变换的各种函数
8
- from .hilbert import hilbert_transform, hilbert_real, hilbert_imaginary
9
- from .hilbert import plot_hilbert, plot_hilbert_complex_plane
10
-
11
- from .process import normalize_signal
12
- from .process import common_dtype
13
- from .process import not_duplicate
14
-
15
- from .process import find_zero_crossings
16
- from .process import get_timeline
17
-
18
- # 实现离散信号差分的算法
19
- from ._differ import differ
20
-
21
- # 实现一维信号平滑的函数
22
- from ._smooth1d import simple_moving_average, weighted_moving_average
23
- from ._smooth1d import gaussian_smoothing, savgol_smoothing, exponential_smoothing
24
-
25
- # 最大最小归一化
26
- from ._function import max_min_normalization
27
- # Z-score 标准化
28
- from ._function import z_score_normalization
29
- # 最大绝对值标准化
30
- from ._function import max_absolute_normalization
31
- # 对数变换
32
- from ._function import log_transformation
33
- # 小数定标标准化
34
- from ._function import decimal_scaling_normalization
1
+ # Fourier Transform
2
+ from ._fft import fft, ifft, fftshift, ifftshift # 傅里叶变换的正变换、逆变换、频谱移动和逆移动
3
+
4
+ # Signal mirroring extension
5
+ from .mirror import fmirror # 信号的镜像扩展
6
+
7
+ # Various functions for Hilbert Transform
8
+ from .hilbert import hilbert_transform, hilbert_real, hilbert_imaginary # 希尔伯特变换的相关函数,包括获取实部和虚部
9
+ from .hilbert import plot_hilbert, plot_hilbert_complex_plane # 绘制希尔伯特变换结果的相关函数
10
+
11
+ from .process import normalize_signal # 归一化信号
12
+ from .process import common_dtype # 检查数据的通用数据类型
13
+ from .process import not_duplicate # 检查数据是否没有重复
14
+
15
+ from .process import find_zero_crossings # 查找信号的零交叉点
16
+ from .process import get_timeline # 获取信号的时间轴
17
+
18
+ # Algorithm for discrete signal differencing
19
+ from ._differ import differ # 实现离散信号差分的算法
20
+
21
+ # Functions for 1D signal smoothing
22
+ from ._smooth1d import simple_moving_average, weighted_moving_average # 实现一维信号的简单移动平均和平滑移动平均
23
+ from ._smooth1d import gaussian_smoothing, savgol_smoothing, exponential_smoothing # 高斯平滑、Savitzky-Golay平滑和指数平滑
24
+
25
+ # Min-max normalization
26
+ from ._function import max_min_normalization # 最大最小归一化
27
+ # Z-score standardization
28
+ from ._function import z_score_normalization # Z-score 标准化
29
+ # Max-absolute normalization
30
+ from ._function import max_absolute_normalization # 最大绝对值标准化
31
+ # Logarithmic transformation
32
+ from ._function import log_transformation # 对数变换
33
+ # Decimal scaling normalization
34
+ from ._function import decimal_scaling_normalization # 小数定标标准化
35
+
pysdkit/vmd/_vmd_c.py CHANGED
@@ -3,6 +3,8 @@
3
3
  Created on Sat Mar 4 11:59:21 2024
4
4
  @author: Whenxuan Wang
5
5
  @email: wwhenxuan@gmail.com
6
+ Original paper: Dragomiretskiy, K. and Zosso, D. (2014) ‘Variational Mode Decomposition’,
7
+ IEEE Transactions on Signal Processing, 62(3), pp. 531–544. doi: 10.1109/TSP.2013.2288675.
6
8
  """
7
9
  import numpy as np
8
10
  from typing import Optional, List
@@ -12,11 +14,7 @@ from ..plot import plot_IMFs
12
14
 
13
15
 
14
16
  class VMD(Base):
15
- """
16
- Variational mode decomposition, object-oriented interface.
17
- Dragomiretskiy, K. and Zosso, D. (2014) ‘Variational Mode Decomposition’,
18
- IEEE Transactions on Signal Processing, 62(3), pp. 531–544. doi: 10.1109/TSP.2013.2288675.
19
- """
17
+ """Variational mode decomposition, object-oriented interface."""
20
18
 
21
19
  def __init__(self, alpha: int, K: int, tau: float, init: str = 'uniform', DC: bool = False,
22
20
  max_iter: int = 500, tol: float = 1e-6):
pysdkit/vmd/_vmd_f.py CHANGED
@@ -3,6 +3,8 @@
3
3
  Created on Sat Mar 4 11:59:21 2024
4
4
  @author: Whenxuan Wang
5
5
  @email: wwhenxuan@gmail.com
6
+ Original paper: Dragomiretskiy, K. and Zosso, D. (2014) ‘Variational Mode Decomposition’,
7
+ IEEE Transactions on Signal Processing, 62(3), pp. 531–544. doi: 10.1109/TSP.2013.2288675.
6
8
  """
7
9
  import numpy as np
8
10
  from ..utils import fft, ifft, fftshift, ifftshift, fmirror
@@ -12,9 +14,6 @@ def vmd(signal: np.array, alpha: int, K: int, tau: float, init: str = 'uniform',
12
14
  max_iter: int = 500, tol: float = 1e-6):
13
15
  """
14
16
  Variational mode decomposition, object-oriented interface.
15
- Original paper:
16
- Dragomiretskiy, K. and Zosso, D. (2014) ‘Variational Mode Decomposition’,
17
- IEEE Transactions on Signal Processing, 62(3), pp. 531–544. doi: 10.1109/TSP.2013.2288675.
18
17
  :param signal: the time domain signal (1D numpy array) to be decomposed
19
18
  :param alpha: the balancing parameter of the data-fidelity constraint
20
19
  :param K: the number of modes to be recovered
pysdkit/ewt/_ewt.py DELETED
@@ -1,449 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- Created on Tue Dec 18 11:45:32 2018
4
- Empirical Wavelet Transform implementation for 1D signals
5
- Original paper:
6
- Gilles, J., 2013. Empirical Wavelet Transform. IEEE Transactions on Signal Processing, 61(16), pp.3999–4010.
7
- Available at: http://ieeexplore.ieee.org/lpdocs/epic03/wrapper.htm?arnumber=6522142.
8
- Original Matlab toolbox: https://www.mathworks.com/matlabcentral/fileexchange/42141-empirical-wavelet-transforms
9
-
10
- @author: Vinícius Rezende Carvalho
11
- Programa de pós graduação em engenharia elétrica - PPGEE UFMG
12
- Universidade Federal de Minas Gerais - Belo Horizonte, Brazil
13
- Núcleo de Neurociências - NNC
14
- """
15
-
16
- import numpy as np
17
-
18
-
19
- # %EWT functions
20
- def EWT1D(f, N=5, log=0, detect="locmax", completion=0, reg='average', lengthFilter=10, sigmaFilter=5):
21
- """
22
- =========================================================================
23
- ewt, mfb ,boundaries = EWT1D(f, N = 5, log = 0,detect = "locmax", completion = 0, reg = 'average', lengthFilter = 10,sigmaFilter = 5):
24
-
25
- Perform the Empirical Wavelet Transform of f over N scales. See
26
- also the documentation of EWT_Boundaries_Detect for more details about
27
- the available methods and their parameters.
28
-
29
- Inputs:
30
- -f: the 1D input signal
31
- Optional Inputs:
32
- -log: 0 or 1 to indicate if we want to work with
33
- the log spectrum
34
- -method: 'locmax','locmaxmin','locmaxminf'
35
- -reg: 'none','gaussian','average'
36
- -lengthFilter: width of the above filters (Gaussian or average)
37
- -sigmaFilter: standard deviation of the above Gaussian filter
38
- -N: maximum number of supports (modes or signal components)
39
- -completion: 0 or 1 to indicate if we try to complete
40
- or not the number of modes if the detection
41
- find a lower number of mode than N
42
- Outputs:
43
- -ewt: contains first the low frequency component and
44
- then the successives frequency subbands
45
- -mfb: contains the filter bank (in the Fourier domain)
46
- -boundaries: vector containing the set of boundaries corresponding
47
- to the Fourier line segmentation (normalized between
48
- 0 and Pi)
49
- Original MATLAB Version:
50
- Author: Jerome Gilles
51
- Institution: UCLA - Department of Mathematics
52
- Year: 2013
53
- Version: 2.0
54
-
55
- Python Version: Vinícius Rezende Carvalho - vrcarva@ufmg.br
56
- Universidade Federal de Minas Gerais - Brasil
57
- Núcleo de Neurociências
58
- % =========================================================================
59
- """
60
- # signal spectrum
61
- ff = np.fft.fft(f)
62
- ff = abs(ff[0:int(np.ceil(ff.size / 2))]) # one-sided magnitude
63
- # extract boundaries of Fourier Segments
64
- boundaries = EWT_Boundaries_Detect(ff, log, detect, N, reg, lengthFilter, sigmaFilter)
65
- boundaries = boundaries * np.pi / round(ff.size)
66
-
67
- if completion == 1 and len(boundaries) < N - 1:
68
- boundaries = EWT_Boundaries_Completion(boundaries, N - 1)
69
- # Filtering
70
- # extend the signal by mirroring to deal with boundaries
71
- ltemp = int(np.ceil(f.size / 2)) # to behave the same as matlab's round
72
- fMirr = np.append(np.flip(f[0:ltemp - 1], axis=0), f)
73
- fMirr = np.append(fMirr, np.flip(f[-ltemp - 1:-1], axis=0))
74
- ffMirr = np.fft.fft(fMirr)
75
-
76
- # build the corresponding filter bank
77
- mfb = EWT_Meyer_FilterBank(boundaries, ffMirr.size)
78
-
79
- # filter the signal to extract each subband
80
- ewt = np.zeros(mfb.shape)
81
- for k in range(mfb.shape[1]):
82
- ewt[:, k] = np.real(np.fft.ifft(np.conjugate(mfb[:, k]) * ffMirr))
83
- ewt = ewt[ltemp - 1:-ltemp, :]
84
-
85
- return ewt, mfb, boundaries
86
-
87
-
88
- def EWT_Boundaries_Detect(ff, log, detect, N, reg, lengthFilter, sigmaFilter):
89
- """This function segments f into a certain amount of supports by using different technics:
90
- - middle point between consecutive local maxima (default),
91
- - lowest minima between consecutive local maxima (locmaxmin),
92
- - lowest minima between consecutive local maxima of original spectrum (locmaxminf),
93
-
94
-
95
- Regularized version of the spectrum can be obtained by the
96
- following methods:
97
- - Gaussian filtering (its parameters are filter of width
98
- lengthFilter and standard deviation sigmaFilter)scalesp
99
- - Average filtering (its parameters are filter of width
100
- lengthFilter)
101
-
102
- Note: the detected boundaries are given in term of indices
103
-
104
- Inputs:
105
- -f: the function to segment
106
- Optional parameters:
107
- -log: 0 or 1 to indicate if we want to work with
108
- the log of the ff
109
- -reg: 'none','gaussian','average'
110
- -lengthFilter: width of the above filters (Gaussian or average)
111
- -sigmaFilter: standard deviation of the above Gaussian filter
112
- -N: maximum number of supports (modes or signal components)
113
- -completion: 0 or 1 to indicate if we try to complete
114
- or not the number of modes if the detection
115
- find a lower number of mode than N
116
-
117
- Outputs:
118
- -boundaries: list of detected boundaries
119
-
120
- TODO Preprocessing steps not yet implemented
121
- """
122
- from scipy.ndimage.filters import gaussian_filter
123
- # apply log if needed
124
- if log == 1:
125
- ff = np.log(ff)
126
-
127
- # Global trend removal - TODO
128
-
129
- # Regularization
130
- if reg == 'average':
131
- regFilter = np.ones(lengthFilter) / lengthFilter
132
- presig = np.convolve(ff, regFilter,
133
- mode='same') # for even lenght, numpy's convolve is shifted when compared with MATLAB's
134
-
135
- elif reg == 'gaussian':
136
- regFilter = np.zeros(lengthFilter)
137
- regFilter[regFilter.size // 2] = 1 # prefer odd filter lengths - otherwise the gaussian is skewed
138
- presig = np.convolve(ff, gaussian_filter(regFilter, sigmaFilter), mode='same')
139
- else:
140
- presig = ff
141
-
142
- # Boundaries detection
143
- if detect == "locmax": # Mid-point between two consecutive local maxima computed on the regularized spectrum
144
- boundaries = LocalMax(presig, N)
145
-
146
- elif detect == "locmaxmin": # extract the lowest local minima between two selected local maxima
147
- boundaries = LocalMaxMin(presig, N)
148
-
149
- elif detect == "locmaxminf": # We extract the lowest local minima on the original spectrum between
150
- # two local maxima selected on the regularized signal
151
- boundaries = LocalMaxMin(presig, N, fm=ff)
152
-
153
- # elif detect == "adaptivereg": #TODO
154
-
155
- return boundaries + 1
156
-
157
-
158
- def LocalMax(ff, N):
159
- """
160
- ================================================================
161
- bound = LocalMax(f,N)
162
-
163
- This function segments f into a maximum of N supports by taking
164
- the middle point between the N largest local maxima.
165
- Note: the detected boundaries are given in term of indices
166
-
167
- Inputs:
168
- -f: the function to segment
169
- -N: maximal number of bands
170
-
171
- Outputs:
172
- -bound: list of detected boundaries
173
-
174
- Original MATLAB version:
175
- Author: Jerome Gilles + Kathryn Heal
176
- Institution: UCLA - Department of Mathematics
177
- Year: 2013
178
- Version: 1.0
179
-
180
- Python Version: Vinícius Rezende Carvalho - vrcarva@ufmg.br
181
- Universidade Federal de Minas Gerais - Brasil
182
- Núcleo de Neurociências
183
- %===============================================================
184
- """
185
- N = N - 1
186
- locmax = np.zeros(ff.size)
187
- locmin = max(ff) * np.ones(ff.size)
188
- for i in np.arange(1, ff.size - 1):
189
- if ff[i - 1] < ff[i] and ff[i] > ff[i + 1]:
190
- locmax[i] = ff[i]
191
- if ff[i - 1] > ff[i] and ff[i] <= ff[i + 1]:
192
- locmin[i] = ff[i]
193
- N = min(N, locmax.size)
194
- # keep the N-th highest maxima
195
- maxidxs = np.sort(locmax.argsort()[::-1][:N])
196
- # middle point between consecutive maxima
197
- bound = np.zeros(N)
198
- for i in range(N):
199
- if i == 0:
200
- a = 0
201
- else:
202
- a = maxidxs[i - 1]
203
- bound[i] = (a + maxidxs[i]) / 2
204
-
205
- return bound
206
-
207
-
208
- def LocalMaxMin(f, N, fm=0):
209
- """
210
- ===================================================================
211
- bound = LocalMaxMin(f,N,fm)
212
-
213
- This function segments f into a maximum of N supports by detecting
214
- the lowest local minima between the N largest local maxima. If the
215
- input fm is provided then the local maxima are computed on f and
216
- the local minima on fm otherwise both are computed on f (this is
217
- useful if you want to compute the maxima on a regularized version
218
- of your signal while detecting the "true" minima).
219
-
220
- Note: the detected boundaries are given in term of indices
221
-
222
- Inputs:
223
- -f: the function to segment
224
- -N: maximal number of bands
225
-
226
- Optional input:
227
- -fm: function on which the local minima will be computed
228
-
229
- Outputs:
230
- -bound: list of detected boundaries
231
-
232
- Author: Jerome Gilles
233
- Institution: UCLA - Department of Mathematics
234
- Year: 2013
235
- Version: 2.0
236
-
237
- Python Version: Vinícius Rezende Carvalho - vrcarva@ufmg.br
238
- Universidade Federal de Minas Gerais - Brasil
239
- Núcleo de Neurociências
240
- ===================================================================
241
- """
242
- locmax = np.zeros(f.size)
243
- if type(fm) == int:
244
- f2 = f
245
- else:
246
- f2 = fm
247
- locmin = max(f2) * np.ones(f2.size)
248
- # detect local minima and maxima
249
- for i in np.arange(1, f.size - 1):
250
- if (f[i - 1] < f[i]) and (f[i] > f[i + 1]):
251
- locmax[i] = f[i]
252
- if (f2[i - 1] > f2[i]) and (f2[i] < f2[i + 1]):
253
- locmin[i] = f2[i]
254
- # keep the N-th highest maxima and their index
255
- bound = np.zeros(N)
256
- if N != -1:
257
- N = N - 1
258
- # keep the N-th highest maxima
259
- Imax = np.sort(locmax.argsort()[::-1][:N])
260
- # detect the lowest minima between two consecutive maxima
261
- for i in range(N):
262
- if i == 0:
263
- a = 1
264
- else:
265
- a = Imax[i - 1]
266
- lmin = np.sort(locmin[a:Imax[i]])
267
- ind = np.argsort(locmin[a:Imax[i]])
268
- tmpp = lmin[0]
269
- n = 0
270
- if n < len(lmin):
271
- n = 1
272
- while ((n < len(lmin)) and (tmpp == lmin[n])):
273
- n = n + 1
274
- bound[i] = a + ind[n // 2] - 1
275
- else:
276
- k = 0
277
- for i in range(locmin):
278
- if locmin[i] < max(f2):
279
- bound[k] = i - 1
280
- k = k + 1
281
- return bound
282
-
283
-
284
- # TODO def Adaptive_Bounds_Adapt():
285
-
286
-
287
- def EWT_Boundaries_Completion(boundaries, NT):
288
- """
289
- ======================================================================
290
- boundaries=EWT_Boundaries_Completion(boundaries,NT)
291
-
292
- This function permits to complete the boundaries vector to get a
293
- total of NT boundaries by equally splitting the last band (highest
294
- frequencies)
295
-
296
- Inputs:
297
- -boundaries: the boundaries vector you want to complete
298
- -NT: the total number of boundaries wanted
299
-
300
- Output:
301
- -boundaries: the completed boundaries vector
302
-
303
- Author: Jerome Gilles
304
- Institution: UCLA - Department of Mathematics
305
- Year: 2013
306
- Version: 1.0
307
-
308
- Python Version: Vinícius Rezende Carvalho - vrcarva@ufmg.br
309
- Universidade Federal de Minas Gerais - Brasil
310
- Núcleo de Neurociências
311
- %======================================================================
312
- """
313
- Nd = NT - len(boundaries)
314
- deltaw = (np.pi - boundaries[-1]) / (Nd + 1)
315
- for k in range(Nd):
316
- boundaries = np.append(boundaries, boundaries[-1] + deltaw)
317
-
318
-
319
- def EWT_Meyer_FilterBank(boundaries, Nsig):
320
- """
321
- =========================================================================
322
- function mfb=EWT_Meyer_FilterBank(boundaries,Nsig)
323
-
324
- This function generate the filter bank (scaling function + wavelets)
325
- corresponding to the provided set of frequency segments
326
-
327
- Input parameters:
328
- -boundaries: vector containing the boundaries of frequency segments (0
329
- and pi must NOT be in this vector)
330
- -Nsig: signal length
331
-
332
- Output:
333
- -mfb: cell containing each filter (in the Fourier domain), the scaling
334
- function comes first and then the successive wavelets
335
-
336
- Author: Jerome Gilles
337
- Institution: UCLA - Department of Mathematics
338
- Year: 2012
339
- Version: 1.0
340
-
341
- Python Version: Vinícius Rezende Carvalho - vrcarva@ufmg.br
342
- Universidade Federal de Minas Gerais - Brasil
343
- Núcleo de Neurociências
344
- =========================================================================
345
- """
346
- Npic = len(boundaries)
347
- # compute gamma
348
- gamma = 1
349
- for k in range(Npic - 1):
350
- r = (boundaries[k + 1] - boundaries[k]) / (boundaries[k + 1] + boundaries[k])
351
- if r < gamma:
352
- gamma = r
353
- r = (np.pi - boundaries[Npic - 1]) / (np.pi + boundaries[Npic - 1])
354
- if r < gamma:
355
- gamma = r
356
- gamma = (1 - 1 / Nsig) * gamma # this ensure that gamma is chosen as strictly less than the min
357
-
358
- mfb = np.zeros([Nsig, Npic + 1])
359
-
360
- # EWT_Meyer_Scaling
361
- Mi = int(np.floor(Nsig / 2))
362
- w = np.fft.fftshift(np.linspace(0, 2 * np.pi - 2 * np.pi / Nsig, num=Nsig))
363
- w[0:Mi] = -2 * np.pi + w[0:Mi]
364
- aw = abs(w)
365
- yms = np.zeros(Nsig)
366
- an = 1. / (2 * gamma * boundaries[0])
367
- pbn = (1. + gamma) * boundaries[0]
368
- mbn = (1. - gamma) * boundaries[0]
369
- for k in range(Nsig):
370
- if aw[k] <= mbn:
371
- yms[k] = 1
372
- elif ((aw[k] >= mbn) and (aw[k] <= pbn)):
373
- yms[k] = np.cos(np.pi * EWT_beta(an * (aw[k] - mbn)) / 2)
374
- yms = np.fft.ifftshift(yms)
375
- mfb[:, 0] = yms
376
-
377
- # generate rest of the wavelets
378
- for k in range(Npic - 1):
379
- mfb[:, k + 1] = EWT_Meyer_Wavelet(boundaries[k], boundaries[k + 1], gamma, Nsig)
380
-
381
- mfb[:, Npic] = EWT_Meyer_Wavelet(boundaries[Npic - 1], np.pi, gamma, Nsig)
382
-
383
- return mfb
384
-
385
-
386
- def EWT_beta(x):
387
- """
388
- Beta = EWT_beta(x)
389
- function used in the construction of Meyer's wavelet
390
- """
391
- if x < 0:
392
- bm = 0
393
- elif x > 1:
394
- bm = 1
395
- else:
396
- bm = (x ** 4) * (35. - 84. * x + 70. * (x ** 2) - 20. * (x ** 3))
397
- return bm
398
-
399
-
400
- def EWT_Meyer_Wavelet(wn, wm, gamma, Nsig):
401
- """
402
- =========================================================
403
- ymw=EWT_Meyer_Wavelet(wn,wm,gamma,N)
404
-
405
- Generate the 1D Meyer wavelet in the Fourier
406
- domain associated to scale segment [wn,wm]
407
- with transition ratio gamma
408
-
409
- Input parameters:
410
- -wn : lower boundary
411
- -wm : upper boundary
412
- -gamma : transition ratio
413
- -N : number of point in the vector
414
-
415
- Output:
416
- -ymw: Fourier transform of the wavelet on the band [wn,wm]
417
-
418
- Author: Jerome Gilles
419
- Institution: UCLA - Department of Mathematics
420
- Year: 2012
421
- Version: 1.0
422
-
423
- Python Version: Vinícius Rezende Carvalho - vrcarva@ufmg.br
424
- Universidade Federal de Minas Gerais - Brasil
425
- Núcleo de Neurociências
426
- ==========================================================
427
- """
428
- Mi = int(np.floor(Nsig / 2))
429
- w = np.fft.fftshift(np.linspace(0, 2 * np.pi - 2 * np.pi / Nsig, num=Nsig))
430
- w[0:Mi] = -2 * np.pi + w[0:Mi]
431
- aw = abs(w)
432
- ymw = np.zeros(Nsig)
433
- an = 1. / (2 * gamma * wn)
434
- am = 1. / (2 * gamma * wm)
435
- pbn = (1. + gamma) * wn
436
- mbn = (1. - gamma) * wn
437
- pbm = (1. + gamma) * wm
438
- mbm = (1. - gamma) * wm
439
-
440
- for k in range(Nsig):
441
- if ((aw[k] >= pbn) and (aw[k] <= mbm)):
442
- ymw[k] = 1
443
- elif ((aw[k] >= mbm) and (aw[k] <= pbm)):
444
- ymw[k] = np.cos(np.pi * EWT_beta(am * (aw[k] - mbm)) / 2)
445
- elif ((aw[k] >= mbn) and (aw[k] <= pbn)):
446
- ymw[k] = np.sin(np.pi * EWT_beta(an * (aw[k] - mbn)) / 2)
447
-
448
- ymw = np.fft.ifftshift(ymw)
449
- return ymw
File without changes