PySDKit 0.4.36__tar.gz → 0.4.38__tar.gz
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.
- {pysdkit-0.4.36 → pysdkit-0.4.38}/PKG-INFO +3 -3
- {pysdkit-0.4.36 → pysdkit-0.4.38}/PySDKit.egg-info/PKG-INFO +3 -3
- {pysdkit-0.4.36 → pysdkit-0.4.38}/PySDKit.egg-info/SOURCES.txt +4 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/README.md +2 -2
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/__init__.py +12 -1
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/semd.py +8 -2
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd2d/__init__.py +2 -0
- pysdkit-0.4.38/pysdkit/_emd2d/bmemd.py +413 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_faemd/faemd.py +2 -6
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_faemd/faemd2d.py +2 -6
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_faemd/faemd3d.py +2 -6
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_faemd/filter.py +4 -12
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_gdmd/__init__.py +21 -1
- pysdkit-0.4.38/pysdkit/_gdmd/vgnmd.py +704 -0
- pysdkit-0.4.38/pysdkit/tests/test_bmemd.py +102 -0
- pysdkit-0.4.38/pysdkit/tests/test_ceemdan.py +202 -0
- pysdkit-0.4.38/pysdkit/tests/test_eemd.py +175 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_faemd.py +0 -1
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_mstl.py +8 -4
- pysdkit-0.4.38/pysdkit/tests/test_vgnmd.py +161 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tsa/_mstl.py +3 -1
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tsa/_stl.py +11 -4
- pysdkit-0.4.36/pysdkit/_emd2d/bmemd.py +0 -24
- pysdkit-0.4.36/pysdkit/tests/test_ceemdan.py +0 -286
- {pysdkit-0.4.36 → pysdkit-0.4.38}/LICENSE +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/PySDKit.egg-info/dependency_links.txt +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/PySDKit.egg-info/requires.txt +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/PySDKit.egg-info/top_level.txt +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pyproject.toml +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_alif/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_alif/alif.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_alif/data/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_alif/data/prefixed_double_filter.npy +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_alif/iterative_filtering.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_apmd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_apmd/apmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/_find_extrema.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/_prepare_points.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/_splines.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/ceemdan.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/eemd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/efd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/emd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/hht/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/hht/frequency.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/hht/hht.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/memd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/remd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd/tvf_emd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd2d/bemd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_emd2d/emd2d.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_esmd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_esmd/esmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_ewt/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_ewt/ewt.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_ewt/ewt2d.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_faemd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_faemd/extrema.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_fmd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_fmd/fmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_fmd/nfmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_gdmd/gdmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_hvd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_hvd/hvd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_itd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_itd/itd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_jmd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_jmd/jmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_jmd/mjmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_jmd/sjmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_lmd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_lmd/lmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_lmd/rlmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_osd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_osd/swd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_ssa/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_ssa/ssa.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/acmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/avmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/ba_acmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/base.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/mvmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/ovmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/stvmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/svmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/vmd_c.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/vmd_f.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd/vme.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd2d/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd2d/cvmd2d.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vmd2d/vmd2d.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vncmd/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vncmd/avncmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vncmd/incmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/_vncmd/vncmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/data/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/data/_add_noise.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/data/_cube.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/data/_generator.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/data/_image.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/data/_models.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/data/_test_univariate.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/data/_time_series.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/data/texture.txt +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/entropy/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/entropy/_approxiamte_entropy.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/entropy/_permutation_entropy.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/entropy/_sample_entropy.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/models/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/models/_base.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/models/_kmeans.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/models/_knn.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/models/_pca.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/plot/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/plot/_fourier_spectra.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/plot/_functions.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/plot/_plot_images.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/plot/_plot_imfs.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/plot/_plot_signal.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/data/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/data/test_generator_cube.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/data/test_generator_image.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/data/test_generator_signal.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/data/test_generator_univariate_signal.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/models/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/models/test_knn.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/models/test_pca.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/run_all.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_alif.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_apmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_avncmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_emd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_ewt.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_ewt2d.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_faemd2d.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_faemd3d.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_gdmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_itd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_jmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_lmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_moving_decomp.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_ovmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_rlmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_semd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_sjmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_ssa.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_stl.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_stvmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_svmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_swd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_vmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_vmd2d.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tests/test_vncmd.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tsa/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tsa/_dtw.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/tsa/_moving_decomp.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/__init__.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_cite.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_correlation.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_diagnalization.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_differ.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_fft.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_function.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_hilbert.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_instantaneous.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_kernel_matrix.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_mirror.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_process.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/pysdkit/utils/_smooth1d.py +0 -0
- {pysdkit-0.4.36 → pysdkit-0.4.38}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PySDKit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.38
|
|
4
4
|
Summary: A Python library for signal decomposition algorithms with a unified interface.
|
|
5
5
|
Author: josefinez, Deeksha Manjunath, Yuan Feng, JacktheFowler
|
|
6
6
|
Author-email: Whenxuan Wang <wwhenxuan@gmail.com>, RuiZhe Wang <3133986068@qq.com>, WenTong Zhao <23049200290@stu.xidian.edu.cn>
|
|
@@ -155,8 +155,8 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
|
|
|
155
155
|
| [`TVF_EMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/tvf_emd.py) (Time Varying Filter Based EMD) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168417301135) | [[code]](https://github.com/stfbnc/pytvfemd/tree/master) | ✔️ |
|
|
156
156
|
| [`EFD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/efd.py) (Empirical Fourier Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/97747-empirical-fourier-decomposition-efd) | ✔️ |
|
|
157
157
|
| [`FAEMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd.py) (Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
|
|
158
|
-
| [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) |
|
|
159
|
-
| [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) |
|
|
158
|
+
| [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
|
|
159
|
+
| [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
|
|
160
160
|
| [`HVD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_hvd/hvd.py) (Hilbert Vibration Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X06001556) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/178804-hilbert-vibration-decomposition?s_tid=FX_rc1_behav) | ✔️ |
|
|
161
161
|
| [`ITD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_itd/itd.py) (Intrinsic Time-Scale Decomposition) | [[paper]](https://royalsocietypublishing.org/doi/10.1098/rspa.2006.1761) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/69380-intrinsic-time-scale-decomposition-itd) | ✔️ |
|
|
162
162
|
| [`ALIF`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_alif/alif.py) (Adaptive Local Iterative Filtering) | [[paper]](https://arxiv.org/abs/1411.6051) | [[code]](https://github.com/Cicone/ALIF) | ✔️ |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PySDKit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.38
|
|
4
4
|
Summary: A Python library for signal decomposition algorithms with a unified interface.
|
|
5
5
|
Author: josefinez, Deeksha Manjunath, Yuan Feng, JacktheFowler
|
|
6
6
|
Author-email: Whenxuan Wang <wwhenxuan@gmail.com>, RuiZhe Wang <3133986068@qq.com>, WenTong Zhao <23049200290@stu.xidian.edu.cn>
|
|
@@ -155,8 +155,8 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
|
|
|
155
155
|
| [`TVF_EMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/tvf_emd.py) (Time Varying Filter Based EMD) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168417301135) | [[code]](https://github.com/stfbnc/pytvfemd/tree/master) | ✔️ |
|
|
156
156
|
| [`EFD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/efd.py) (Empirical Fourier Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/97747-empirical-fourier-decomposition-efd) | ✔️ |
|
|
157
157
|
| [`FAEMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd.py) (Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
|
|
158
|
-
| [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) |
|
|
159
|
-
| [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) |
|
|
158
|
+
| [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
|
|
159
|
+
| [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
|
|
160
160
|
| [`HVD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_hvd/hvd.py) (Hilbert Vibration Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X06001556) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/178804-hilbert-vibration-decomposition?s_tid=FX_rc1_behav) | ✔️ |
|
|
161
161
|
| [`ITD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_itd/itd.py) (Intrinsic Time-Scale Decomposition) | [[paper]](https://royalsocietypublishing.org/doi/10.1098/rspa.2006.1761) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/69380-intrinsic-time-scale-decomposition-itd) | ✔️ |
|
|
162
162
|
| [`ALIF`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_alif/alif.py) (Adaptive Local Iterative Filtering) | [[paper]](https://arxiv.org/abs/1411.6051) | [[code]](https://github.com/Cicone/ALIF) | ✔️ |
|
|
@@ -49,6 +49,7 @@ pysdkit/_fmd/fmd.py
|
|
|
49
49
|
pysdkit/_fmd/nfmd.py
|
|
50
50
|
pysdkit/_gdmd/__init__.py
|
|
51
51
|
pysdkit/_gdmd/gdmd.py
|
|
52
|
+
pysdkit/_gdmd/vgnmd.py
|
|
52
53
|
pysdkit/_hvd/__init__.py
|
|
53
54
|
pysdkit/_hvd/hvd.py
|
|
54
55
|
pysdkit/_itd/__init__.py
|
|
@@ -112,7 +113,9 @@ pysdkit/tests/run_all.py
|
|
|
112
113
|
pysdkit/tests/test_alif.py
|
|
113
114
|
pysdkit/tests/test_apmd.py
|
|
114
115
|
pysdkit/tests/test_avncmd.py
|
|
116
|
+
pysdkit/tests/test_bmemd.py
|
|
115
117
|
pysdkit/tests/test_ceemdan.py
|
|
118
|
+
pysdkit/tests/test_eemd.py
|
|
116
119
|
pysdkit/tests/test_emd.py
|
|
117
120
|
pysdkit/tests/test_ewt.py
|
|
118
121
|
pysdkit/tests/test_ewt2d.py
|
|
@@ -134,6 +137,7 @@ pysdkit/tests/test_stl.py
|
|
|
134
137
|
pysdkit/tests/test_stvmd.py
|
|
135
138
|
pysdkit/tests/test_svmd.py
|
|
136
139
|
pysdkit/tests/test_swd.py
|
|
140
|
+
pysdkit/tests/test_vgnmd.py
|
|
137
141
|
pysdkit/tests/test_vmd.py
|
|
138
142
|
pysdkit/tests/test_vmd2d.py
|
|
139
143
|
pysdkit/tests/test_vncmd.py
|
|
@@ -101,8 +101,8 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
|
|
|
101
101
|
| [`TVF_EMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/tvf_emd.py) (Time Varying Filter Based EMD) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168417301135) | [[code]](https://github.com/stfbnc/pytvfemd/tree/master) | ✔️ |
|
|
102
102
|
| [`EFD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/efd.py) (Empirical Fourier Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/97747-empirical-fourier-decomposition-efd) | ✔️ |
|
|
103
103
|
| [`FAEMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd.py) (Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
|
|
104
|
-
| [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) |
|
|
105
|
-
| [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) |
|
|
104
|
+
| [`FAEMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd2d.py) (Two-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
|
|
105
|
+
| [`FAEMD3D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_faemd/faemd3d.py) (Three-Dimensional Fast and Adaptive EMD) | [[paper]](https://ieeexplore.ieee.org/document/8447300) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/71270-fast-and-adaptive-multivariate-and-multidimensional-emd) | ✔️ |
|
|
106
106
|
| [`HVD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_hvd/hvd.py) (Hilbert Vibration Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X06001556) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/178804-hilbert-vibration-decomposition?s_tid=FX_rc1_behav) | ✔️ |
|
|
107
107
|
| [`ITD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_itd/itd.py) (Intrinsic Time-Scale Decomposition) | [[paper]](https://royalsocietypublishing.org/doi/10.1098/rspa.2006.1761) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/69380-intrinsic-time-scale-decomposition-itd) | ✔️ |
|
|
108
108
|
| [`ALIF`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_alif/alif.py) (Adaptive Local Iterative Filtering) | [[paper]](https://arxiv.org/abs/1411.6051) | [[code]](https://github.com/Cicone/ALIF) | ✔️ |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
A Python library for signal decomposition algorithms.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
__version__ = "0.4.
|
|
5
|
+
__version__ = "0.4.38"
|
|
6
6
|
|
|
7
7
|
# Empirical Mode Decomposition
|
|
8
8
|
from ._emd import EMD
|
|
@@ -34,6 +34,9 @@ from ._faemd import FAEMD, FAEMD2D, FAEMD3D
|
|
|
34
34
|
# Empirical Mode Decomposition 2D for images
|
|
35
35
|
from ._emd2d import EMD2D
|
|
36
36
|
|
|
37
|
+
# Bidimensional Multivariate Empirical Mode Decomposition
|
|
38
|
+
from ._emd2d import BMEMD
|
|
39
|
+
|
|
37
40
|
# Hilbert Vibration Decomposition
|
|
38
41
|
from ._hvd import HVD
|
|
39
42
|
|
|
@@ -55,6 +58,9 @@ from ._osd import SWD, swd
|
|
|
55
58
|
# Generalized Dispersion Mode Decomposition
|
|
56
59
|
from ._gdmd import GDMD, gdmd
|
|
57
60
|
|
|
61
|
+
# Variational Generalized Nonlinear Mode Decomposition
|
|
62
|
+
from ._gdmd import VGNMD, vgnmd
|
|
63
|
+
|
|
58
64
|
# Variational Mode Decomposition
|
|
59
65
|
from ._vmd import vmd, VMD
|
|
60
66
|
|
|
@@ -163,6 +169,7 @@ Fast and Adaptive Empirical Mode Decomposition | FAEMD
|
|
|
163
169
|
Bidimensional FAEMD | FAEMD2D
|
|
164
170
|
Tridimensional FAEMD | FAEMD3D
|
|
165
171
|
Empirical Mode Decomposition 2D for images | EMD2D
|
|
172
|
+
Bidimensional Multivariate EMD | BMEMD
|
|
166
173
|
Hilbert Vibration Decomposition | HVD
|
|
167
174
|
Intrinsic Time-Scale Decomposition | ITD
|
|
168
175
|
Local Mean Decomposition | LMD
|
|
@@ -170,6 +177,7 @@ Robust Local Mean Decomposition | RLMD
|
|
|
170
177
|
Singular Spectral Analysis | SSA
|
|
171
178
|
Swarm Decomposition | SWD
|
|
172
179
|
Generalized Dispersion Mode Decomposition | GDMD
|
|
180
|
+
Variational Generalized Nonlinear Mode Dec. | VGNMD
|
|
173
181
|
Variational Mode Decomposition | VMD
|
|
174
182
|
Multivariate Variational Mode Decomposition | MVMD
|
|
175
183
|
Variational Mode Extraction | VME
|
|
@@ -211,6 +219,7 @@ __all__ = [
|
|
|
211
219
|
"FAEMD2D",
|
|
212
220
|
"FAEMD3D",
|
|
213
221
|
"EMD2D",
|
|
222
|
+
"BMEMD",
|
|
214
223
|
"HVD",
|
|
215
224
|
"ITD",
|
|
216
225
|
"LMD",
|
|
@@ -220,6 +229,8 @@ __all__ = [
|
|
|
220
229
|
"swd",
|
|
221
230
|
"GDMD",
|
|
222
231
|
"gdmd",
|
|
232
|
+
"VGNMD",
|
|
233
|
+
"vgnmd",
|
|
223
234
|
"vmd",
|
|
224
235
|
"VMD",
|
|
225
236
|
"ACMD",
|
|
@@ -37,7 +37,9 @@ def concatenate_signals(matrix_x: np.ndarray, num_interval: int) -> np.ndarray:
|
|
|
37
37
|
"""
|
|
38
38
|
matrix_x = np.asarray(matrix_x, dtype=float)
|
|
39
39
|
if matrix_x.ndim != 2:
|
|
40
|
-
raise ValueError(
|
|
40
|
+
raise ValueError(
|
|
41
|
+
"matrix_x must be a 2-D array of shape (n_samples, n_channels)"
|
|
42
|
+
)
|
|
41
43
|
|
|
42
44
|
n_length, n_signal = matrix_x.shape
|
|
43
45
|
if n_signal < 1:
|
|
@@ -276,7 +278,11 @@ class SEMD(object):
|
|
|
276
278
|
if max_imfs is None:
|
|
277
279
|
max_imfs = self.max_imfs
|
|
278
280
|
# Only forward a hard cap when it is positive; otherwise let the backend decide
|
|
279
|
-
emd_kwargs =
|
|
281
|
+
emd_kwargs = (
|
|
282
|
+
{}
|
|
283
|
+
if (max_imfs is None or int(max_imfs) < 0)
|
|
284
|
+
else {"max_imfs": int(max_imfs)}
|
|
285
|
+
)
|
|
280
286
|
|
|
281
287
|
if x.ndim == 1:
|
|
282
288
|
imfs = self.emd.fit_transform(x, **emd_kwargs)
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on 2025/02/05 13:18:18
|
|
4
|
+
@author: Whenxuan Wang
|
|
5
|
+
@email: wwhenxuan@gmail.com
|
|
6
|
+
|
|
7
|
+
Bidimensional Multivariate Empirical Mode Decomposition (BMEMD)
|
|
8
|
+
|
|
9
|
+
Xia, Y., Zhang, B., Pei, W., and Mandic, D. P. (2019).
|
|
10
|
+
Bidimensional Multivariate Empirical Mode Decomposition with Applications
|
|
11
|
+
in Multi-Scale Image Fusion. IEEE Access, 7:114261–114270.
|
|
12
|
+
|
|
13
|
+
MATLAB reference:
|
|
14
|
+
https://github.com/z-bingo/Bidimensional-Multivariate-Empirical-Mode-Decomposition
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import List, Optional, Sequence, Tuple, Union
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
from scipy.interpolate import CloughTocher2DInterpolator, NearestNDInterpolator
|
|
23
|
+
from scipy.ndimage import maximum_filter, minimum_filter, uniform_filter
|
|
24
|
+
|
|
25
|
+
from pysdkit._emd.memd import hamm, nth_prime
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BMEMD(object):
|
|
29
|
+
"""
|
|
30
|
+
Bidimensional Multivariate Empirical Mode Decomposition (BMEMD)
|
|
31
|
+
|
|
32
|
+
Projects a multi-channel image onto direction vectors on the
|
|
33
|
+
``(n-1)``-sphere, finds 2-D extrema of each projected surface, builds
|
|
34
|
+
multivariate envelopes by Delaunay-based surface interpolation, and
|
|
35
|
+
sifts until a MEMD-style stop criterion is met. All channels share the
|
|
36
|
+
same BIMF count, which enables multi-scale image fusion.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
n_dir: int = 8,
|
|
42
|
+
max_imfs: int = 4,
|
|
43
|
+
stop_crit: str = "stop",
|
|
44
|
+
stop_vec: Optional[Sequence[float]] = None,
|
|
45
|
+
stop_cnt: int = 2,
|
|
46
|
+
max_iter: int = 1000,
|
|
47
|
+
max_sift: int = 50,
|
|
48
|
+
) -> None:
|
|
49
|
+
"""
|
|
50
|
+
:param n_dir: Number of projection directions (``>= 6``; MATLAB default 8)
|
|
51
|
+
:param max_imfs: Maximum number of oscillatory BIMFs before the residue
|
|
52
|
+
:param stop_crit: ``"stop"`` (sd / sd2 / tol) or ``"fix_h"``
|
|
53
|
+
:param stop_vec: ``[sd, sd2, tol]`` when ``stop_crit="stop"``;
|
|
54
|
+
default ``[0.01, 0.1, 0.01]`` (MATLAB BMEMD default)
|
|
55
|
+
:param stop_cnt: Fixed sifting count when ``stop_crit="fix_h"``
|
|
56
|
+
:param max_iter: Hard cap on outer BIMF extraction iterations
|
|
57
|
+
:param max_sift: Hard cap on inner sifting iterations per BIMF
|
|
58
|
+
"""
|
|
59
|
+
if not isinstance(n_dir, (int, np.integer)) or n_dir < 6:
|
|
60
|
+
raise ValueError("n_dir must be an integer >= 6")
|
|
61
|
+
if not isinstance(max_imfs, (int, np.integer)) or max_imfs < 1:
|
|
62
|
+
raise ValueError("max_imfs must be a positive integer")
|
|
63
|
+
if stop_crit not in ("stop", "fix_h"):
|
|
64
|
+
raise ValueError("stop_crit must be 'stop' or 'fix_h'")
|
|
65
|
+
|
|
66
|
+
self.n_dir = int(n_dir)
|
|
67
|
+
self.max_imfs = int(max_imfs)
|
|
68
|
+
self.stop_crit = stop_crit
|
|
69
|
+
if stop_vec is None:
|
|
70
|
+
stop_vec = (0.01, 0.1, 0.01)
|
|
71
|
+
if len(stop_vec) != 3:
|
|
72
|
+
raise ValueError("stop_vec must contain three elements [sd, sd2, tol]")
|
|
73
|
+
self.sd, self.sd2, self.tol = map(float, stop_vec)
|
|
74
|
+
self.stop_cnt = int(stop_cnt)
|
|
75
|
+
self.max_iter = int(max_iter)
|
|
76
|
+
self.max_sift = int(max_sift)
|
|
77
|
+
|
|
78
|
+
self.imfs: Optional[np.ndarray] = None
|
|
79
|
+
self.residue: Optional[np.ndarray] = None
|
|
80
|
+
|
|
81
|
+
def __str__(self) -> str:
|
|
82
|
+
return "Bidimensional Multivariate Empirical Mode Decomposition (BMEMD)"
|
|
83
|
+
|
|
84
|
+
def __call__(
|
|
85
|
+
self, images: np.ndarray, max_imfs: Optional[int] = None
|
|
86
|
+
) -> np.ndarray:
|
|
87
|
+
return self.fit_transform(images, max_imfs=max_imfs)
|
|
88
|
+
|
|
89
|
+
# ------------------------------------------------------------------ #
|
|
90
|
+
# Public API
|
|
91
|
+
# ------------------------------------------------------------------ #
|
|
92
|
+
def fit_transform(
|
|
93
|
+
self, images: np.ndarray, max_imfs: Optional[int] = None
|
|
94
|
+
) -> np.ndarray:
|
|
95
|
+
"""
|
|
96
|
+
Decompose a multi-channel image stack.
|
|
97
|
+
|
|
98
|
+
:param images: Array of shape ``(n_channels, H, W)`` with
|
|
99
|
+
``2 <= n_channels <= 16``
|
|
100
|
+
:param max_imfs: Optional override for the number of oscillatory BIMFs
|
|
101
|
+
:return: ``(K, n_channels, H, W)`` where the last slice is the residue
|
|
102
|
+
"""
|
|
103
|
+
x = self._check_input(images)
|
|
104
|
+
n_ch, height, width = x.shape
|
|
105
|
+
max_imfs = self.max_imfs if max_imfs is None else int(max_imfs)
|
|
106
|
+
|
|
107
|
+
directions = self._direction_vectors(n_ch)
|
|
108
|
+
residue = x.astype(float, copy=True)
|
|
109
|
+
modes: List[np.ndarray] = []
|
|
110
|
+
|
|
111
|
+
for _ in range(max_imfs):
|
|
112
|
+
if self._stop_emd(residue, directions):
|
|
113
|
+
break
|
|
114
|
+
|
|
115
|
+
mode = residue.copy()
|
|
116
|
+
sift_i = 0
|
|
117
|
+
n_h = 0 # consecutive OK counts for fix_h
|
|
118
|
+
|
|
119
|
+
while sift_i < self.max_sift:
|
|
120
|
+
sift_i += 1
|
|
121
|
+
try:
|
|
122
|
+
env_mean, nem, amp = self._envelope_mean(mode, directions)
|
|
123
|
+
except Exception:
|
|
124
|
+
env_mean = np.zeros_like(mode)
|
|
125
|
+
break
|
|
126
|
+
|
|
127
|
+
if self.stop_crit == "stop":
|
|
128
|
+
if self._stop_sifting(env_mean, amp, nem):
|
|
129
|
+
break
|
|
130
|
+
else: # fix_h
|
|
131
|
+
# MEMD-style: count successive siftings with enough extrema
|
|
132
|
+
if nem > 9:
|
|
133
|
+
n_h += 1
|
|
134
|
+
else:
|
|
135
|
+
n_h = 0
|
|
136
|
+
if n_h >= self.stop_cnt:
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
mode = mode - env_mean
|
|
140
|
+
|
|
141
|
+
modes.append(mode)
|
|
142
|
+
residue = residue - mode
|
|
143
|
+
if sift_i >= self.max_sift:
|
|
144
|
+
break
|
|
145
|
+
|
|
146
|
+
modes.append(residue)
|
|
147
|
+
imfs = np.stack(modes, axis=0)
|
|
148
|
+
self.imfs = imfs[:-1]
|
|
149
|
+
self.residue = residue
|
|
150
|
+
return imfs
|
|
151
|
+
|
|
152
|
+
def fuse(
|
|
153
|
+
self,
|
|
154
|
+
images: np.ndarray,
|
|
155
|
+
imfs: Optional[np.ndarray] = None,
|
|
156
|
+
var_window: int = 5,
|
|
157
|
+
) -> np.ndarray:
|
|
158
|
+
"""
|
|
159
|
+
Multi-scale image fusion via local-variance weights (Xia et al.).
|
|
160
|
+
|
|
161
|
+
For each oscillatory BIMF, channel weights are proportional to the
|
|
162
|
+
local squared deviation from a moving mean (MATLAB ``local_var_img``).
|
|
163
|
+
The residue is fused by intensity proportions.
|
|
164
|
+
|
|
165
|
+
:param images: ``(n_channels, H, W)`` input stack (used if ``imfs`` is None)
|
|
166
|
+
:param imfs: Optional precomputed BIMFs ``(K, n_channels, H, W)``
|
|
167
|
+
:param var_window: Odd window size for local variance (default 5)
|
|
168
|
+
:return: Fused grayscale image ``(H, W)``
|
|
169
|
+
"""
|
|
170
|
+
if imfs is None:
|
|
171
|
+
imfs = self.fit_transform(images)
|
|
172
|
+
imfs = np.asarray(imfs, dtype=float)
|
|
173
|
+
if imfs.ndim != 4:
|
|
174
|
+
raise ValueError("imfs must have shape (K, n_channels, H, W)")
|
|
175
|
+
|
|
176
|
+
k_modes, n_ch, height, width = imfs.shape
|
|
177
|
+
fused = np.zeros((height, width), dtype=float)
|
|
178
|
+
|
|
179
|
+
for q in range(k_modes):
|
|
180
|
+
bimf = imfs[q] # (C, H, W)
|
|
181
|
+
if q < k_modes - 1:
|
|
182
|
+
var = local_var_img(bimf, var_window) # (C, H, W)
|
|
183
|
+
denom = np.sum(var, axis=0, keepdims=True) + 1e-12
|
|
184
|
+
weights = var / denom
|
|
185
|
+
else:
|
|
186
|
+
denom = np.sum(bimf, axis=0, keepdims=True)
|
|
187
|
+
# Avoid division by zero on flat residue
|
|
188
|
+
denom = np.where(np.abs(denom) < 1e-12, 1.0, denom)
|
|
189
|
+
weights = bimf / denom
|
|
190
|
+
fused = fused + np.sum(bimf * weights, axis=0)
|
|
191
|
+
|
|
192
|
+
return fused
|
|
193
|
+
|
|
194
|
+
# ------------------------------------------------------------------ #
|
|
195
|
+
# Internals
|
|
196
|
+
# ------------------------------------------------------------------ #
|
|
197
|
+
@staticmethod
|
|
198
|
+
def _check_input(images: np.ndarray) -> np.ndarray:
|
|
199
|
+
x = np.asarray(images, dtype=float)
|
|
200
|
+
if x.ndim != 3:
|
|
201
|
+
raise ValueError("BMEMD expects a 3-D array of shape (n_channels, H, W)")
|
|
202
|
+
n_ch, height, width = x.shape
|
|
203
|
+
if n_ch < 2 or n_ch > 16:
|
|
204
|
+
raise ValueError("n_channels must satisfy 2 <= n_channels <= 16")
|
|
205
|
+
if height < 3 or width < 3:
|
|
206
|
+
raise ValueError("Each spatial dimension must be >= 3")
|
|
207
|
+
return x
|
|
208
|
+
|
|
209
|
+
def _direction_vectors(self, n_dim: int) -> np.ndarray:
|
|
210
|
+
"""
|
|
211
|
+
Unit directions of shape ``(n_dir, n_dim)``.
|
|
212
|
+
|
|
213
|
+
Matches MATLAB ``get_dir`` / Hammersley construction in ``bmemd.m``.
|
|
214
|
+
"""
|
|
215
|
+
ndir = self.n_dir
|
|
216
|
+
dirs = np.zeros((ndir, n_dim), dtype=float)
|
|
217
|
+
|
|
218
|
+
if n_dim == 2:
|
|
219
|
+
# Uniform samples on the circle (MATLAB 1-based index)
|
|
220
|
+
for it in range(1, ndir + 1):
|
|
221
|
+
dirs[it - 1, 0] = np.cos(2.0 * np.pi * it / ndir)
|
|
222
|
+
dirs[it - 1, 1] = np.sin(2.0 * np.pi * it / ndir)
|
|
223
|
+
return dirs
|
|
224
|
+
|
|
225
|
+
# Hammersley low-discrepancy sequence
|
|
226
|
+
if n_dim == 3:
|
|
227
|
+
base = [-ndir, 2]
|
|
228
|
+
seq = np.zeros((2, ndir))
|
|
229
|
+
for it in range(2):
|
|
230
|
+
seq[it, :] = np.asarray(hamm(ndir, base[it])).ravel()
|
|
231
|
+
for it in range(ndir):
|
|
232
|
+
tt = float(np.clip(2.0 * seq[0, it] - 1.0, -1.0, 1.0))
|
|
233
|
+
phirad = float(seq[1, it] * 2.0 * np.pi)
|
|
234
|
+
st = np.sqrt(max(1.0 - tt * tt, 0.0))
|
|
235
|
+
dirs[it, 0] = st * np.cos(phirad)
|
|
236
|
+
dirs[it, 1] = st * np.sin(phirad)
|
|
237
|
+
dirs[it, 2] = tt
|
|
238
|
+
return dirs
|
|
239
|
+
|
|
240
|
+
# n_dim > 3 (same construction as MEMD / MATLAB bmemd.m)
|
|
241
|
+
primes = nth_prime(n_dim - 1)
|
|
242
|
+
base = [-ndir] + list(primes[: n_dim - 1])
|
|
243
|
+
seq = np.zeros((n_dim, ndir))
|
|
244
|
+
for it in range(n_dim):
|
|
245
|
+
seq[it, :] = np.asarray(hamm(ndir, base[it])).ravel()
|
|
246
|
+
|
|
247
|
+
for it in range(ndir):
|
|
248
|
+
b = 2.0 * seq[:, it] - 1.0
|
|
249
|
+
# atan2(sqrt(flipud(cumsum(b(end:-1:2).^2))), b(1:end-1))
|
|
250
|
+
tht = np.arctan2(
|
|
251
|
+
np.sqrt(np.flipud(np.cumsum(b[:0:-1] ** 2))), b[: n_dim - 1]
|
|
252
|
+
)
|
|
253
|
+
dir_t = np.cumprod(np.concatenate(([1.0], np.sin(tht))))
|
|
254
|
+
dir_t = np.asarray(dir_t[:n_dim], dtype=float)
|
|
255
|
+
dir_t[: n_dim - 1] = np.cos(tht) * dir_t[: n_dim - 1]
|
|
256
|
+
dirs[it] = dir_t
|
|
257
|
+
return dirs
|
|
258
|
+
|
|
259
|
+
@staticmethod
|
|
260
|
+
def _project(images: np.ndarray, direction: np.ndarray) -> np.ndarray:
|
|
261
|
+
"""Pixel-wise projection ``Σ_c I_c * u_c`` → shape ``(H, W)``."""
|
|
262
|
+
return np.tensordot(direction, images, axes=(0, 0))
|
|
263
|
+
|
|
264
|
+
@staticmethod
|
|
265
|
+
def _regional_extrema(surface: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
266
|
+
"""Boolean maps of regional maxima / minima (MATLAB ``imregional*``)."""
|
|
267
|
+
maxima = maximum_filter(surface, size=3) == surface
|
|
268
|
+
minima = minimum_filter(surface, size=3) == surface
|
|
269
|
+
return maxima, minima
|
|
270
|
+
|
|
271
|
+
def _stop_emd(self, residue: np.ndarray, directions: np.ndarray) -> bool:
|
|
272
|
+
"""Stop if any projection has fewer than 3 maxima or minima."""
|
|
273
|
+
for d in directions:
|
|
274
|
+
y = self._project(residue, d)
|
|
275
|
+
maxima, minima = self._regional_extrema(y)
|
|
276
|
+
if maxima.sum() < 3 or minima.sum() < 3:
|
|
277
|
+
return True
|
|
278
|
+
return False
|
|
279
|
+
|
|
280
|
+
def _envelope_mean(
|
|
281
|
+
self, mode: np.ndarray, directions: np.ndarray
|
|
282
|
+
) -> Tuple[np.ndarray, int, np.ndarray]:
|
|
283
|
+
"""
|
|
284
|
+
Average multivariate envelopes over all projection directions.
|
|
285
|
+
|
|
286
|
+
:return: ``(env_mean, nem_last, amp)`` where ``amp`` is the summed
|
|
287
|
+
envelope amplitude map used by the stop criterion.
|
|
288
|
+
"""
|
|
289
|
+
n_ch, height, width = mode.shape
|
|
290
|
+
env_mean = np.zeros_like(mode)
|
|
291
|
+
amp = np.zeros((height, width), dtype=float)
|
|
292
|
+
nem = 0
|
|
293
|
+
|
|
294
|
+
yy_grid, xx_grid = np.mgrid[0:height, 0:width]
|
|
295
|
+
|
|
296
|
+
for d in directions:
|
|
297
|
+
y = self._project(mode, d)
|
|
298
|
+
maxima, minima = self._regional_extrema(y)
|
|
299
|
+
nem = int(maxima.sum() + minima.sum())
|
|
300
|
+
|
|
301
|
+
max_r, max_c = np.nonzero(maxima)
|
|
302
|
+
min_r, min_c = np.nonzero(minima)
|
|
303
|
+
if max_r.size < 3 or min_r.size < 3:
|
|
304
|
+
raise RuntimeError("Insufficient extrema for envelope fitting")
|
|
305
|
+
|
|
306
|
+
env_max = np.zeros_like(mode)
|
|
307
|
+
env_min = np.zeros_like(mode)
|
|
308
|
+
for c in range(n_ch):
|
|
309
|
+
env_max[c] = _surface_from_points(
|
|
310
|
+
max_c, max_r, mode[c, max_r, max_c], xx_grid, yy_grid
|
|
311
|
+
)
|
|
312
|
+
env_min[c] = _surface_from_points(
|
|
313
|
+
min_c, min_r, mode[c, min_r, min_c], xx_grid, yy_grid
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
amp = amp + np.sqrt(np.sum((env_max - env_min) ** 2, axis=0))
|
|
317
|
+
env_mean = env_mean + 0.5 * (env_max + env_min)
|
|
318
|
+
|
|
319
|
+
env_mean = env_mean / float(len(directions))
|
|
320
|
+
return env_mean, nem, amp
|
|
321
|
+
|
|
322
|
+
def _stop_sifting(self, env_mean: np.ndarray, amp: np.ndarray, nem: int) -> bool:
|
|
323
|
+
"""Return True if sifting should stop (MATLAB ``stop_sifting``)."""
|
|
324
|
+
sx = np.sqrt(np.sum(env_mean**2, axis=0))
|
|
325
|
+
if np.any(amp):
|
|
326
|
+
sx = sx / (amp + 1e-12)
|
|
327
|
+
continue_sift = (
|
|
328
|
+
(np.mean(sx > self.sd) > self.tol) or np.any(sx > self.sd2)
|
|
329
|
+
) and (nem > 9)
|
|
330
|
+
return not continue_sift
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _surface_from_points(
|
|
334
|
+
x: np.ndarray,
|
|
335
|
+
y: np.ndarray,
|
|
336
|
+
z: np.ndarray,
|
|
337
|
+
xx_grid: np.ndarray,
|
|
338
|
+
yy_grid: np.ndarray,
|
|
339
|
+
) -> np.ndarray:
|
|
340
|
+
"""
|
|
341
|
+
Interpolate scattered extrema onto the full image grid.
|
|
342
|
+
|
|
343
|
+
Uses Clough–Tocher (Delaunay-based) interpolation as recommended in the
|
|
344
|
+
BMEMD paper, with nearest-neighbour fill for exterior / degenerate regions.
|
|
345
|
+
"""
|
|
346
|
+
points = np.column_stack([x.astype(float), y.astype(float)])
|
|
347
|
+
values = z.astype(float)
|
|
348
|
+
|
|
349
|
+
# Deduplicate coincident extrema (keep mean value)
|
|
350
|
+
_, unique_idx = np.unique(points, axis=0, return_index=True)
|
|
351
|
+
points = points[unique_idx]
|
|
352
|
+
values = values[unique_idx]
|
|
353
|
+
|
|
354
|
+
if points.shape[0] < 3:
|
|
355
|
+
return np.full(xx_grid.shape, float(np.mean(values)))
|
|
356
|
+
|
|
357
|
+
try:
|
|
358
|
+
interp = CloughTocher2DInterpolator(points, values, fill_value=np.nan)
|
|
359
|
+
surface = interp(xx_grid, yy_grid)
|
|
360
|
+
except Exception:
|
|
361
|
+
surface = np.full(xx_grid.shape, np.nan, dtype=float)
|
|
362
|
+
|
|
363
|
+
if np.any(~np.isfinite(surface)):
|
|
364
|
+
nearest = NearestNDInterpolator(points, values)
|
|
365
|
+
nan_mask = ~np.isfinite(surface)
|
|
366
|
+
surface[nan_mask] = nearest(xx_grid[nan_mask], yy_grid[nan_mask])
|
|
367
|
+
|
|
368
|
+
return surface
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def local_var_img(images: np.ndarray, window: int = 5) -> np.ndarray:
|
|
372
|
+
"""
|
|
373
|
+
Local squared deviation from a moving mean (MATLAB ``local_var_img``).
|
|
374
|
+
|
|
375
|
+
:param images: ``(H, W)`` or ``(C, H, W)``
|
|
376
|
+
:param window: Odd filter size
|
|
377
|
+
:return: Same shape as ``images``
|
|
378
|
+
"""
|
|
379
|
+
x = np.asarray(images, dtype=float)
|
|
380
|
+
w = int(window)
|
|
381
|
+
if w < 1:
|
|
382
|
+
raise ValueError("window must be a positive integer")
|
|
383
|
+
|
|
384
|
+
if x.ndim == 2:
|
|
385
|
+
mean = uniform_filter(x, size=w, mode="nearest")
|
|
386
|
+
return (x - mean) ** 2
|
|
387
|
+
|
|
388
|
+
if x.ndim == 3:
|
|
389
|
+
out = np.empty_like(x)
|
|
390
|
+
for i in range(x.shape[0]):
|
|
391
|
+
mean = uniform_filter(x[i], size=w, mode="nearest")
|
|
392
|
+
out[i] = (x[i] - mean) ** 2
|
|
393
|
+
return out
|
|
394
|
+
|
|
395
|
+
raise ValueError("images must be 2-D or 3-D")
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def fuse_images(
|
|
399
|
+
images: np.ndarray,
|
|
400
|
+
n_dir: int = 8,
|
|
401
|
+
max_imfs: int = 4,
|
|
402
|
+
var_window: int = 5,
|
|
403
|
+
**bmemd_kwargs,
|
|
404
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
405
|
+
"""
|
|
406
|
+
Convenience wrapper: BMEMD decomposition + variance-weighted fusion.
|
|
407
|
+
|
|
408
|
+
:return: ``(fused_image, imfs)``
|
|
409
|
+
"""
|
|
410
|
+
bmemd = BMEMD(n_dir=n_dir, max_imfs=max_imfs, **bmemd_kwargs)
|
|
411
|
+
imfs = bmemd.fit_transform(images)
|
|
412
|
+
fused = bmemd.fuse(images, imfs=imfs, var_window=var_window)
|
|
413
|
+
return fused, imfs
|
|
@@ -163,9 +163,7 @@ class FAEMD(object):
|
|
|
163
163
|
|
|
164
164
|
def get_imfs_and_residue(self) -> Tuple[np.ndarray, np.ndarray]:
|
|
165
165
|
if self.imfs is None or self.residue is None:
|
|
166
|
-
raise ValueError(
|
|
167
|
-
"No IMF found. Please run `fit_transform` method first."
|
|
168
|
-
)
|
|
166
|
+
raise ValueError("No IMF found. Please run `fit_transform` method first.")
|
|
169
167
|
return self.imfs, self.residue
|
|
170
168
|
|
|
171
169
|
|
|
@@ -178,9 +176,7 @@ def check_inputs(signal: np.ndarray) -> Tuple[np.ndarray, Tuple]:
|
|
|
178
176
|
elif signal.ndim == 2:
|
|
179
177
|
pass
|
|
180
178
|
else:
|
|
181
|
-
raise ValueError(
|
|
182
|
-
"signal must have shape [seq_len] or [n_channels, seq_len]"
|
|
183
|
-
)
|
|
179
|
+
raise ValueError("signal must have shape [seq_len] or [n_channels, seq_len]")
|
|
184
180
|
return signal.T, inputs_shape
|
|
185
181
|
|
|
186
182
|
|
|
@@ -60,9 +60,7 @@ class FAEMD2D(object):
|
|
|
60
60
|
self.residue: Optional[np.ndarray] = None
|
|
61
61
|
|
|
62
62
|
def __str__(self) -> str:
|
|
63
|
-
return (
|
|
64
|
-
"Bidimensional Fast and Adaptive Empirical Mode Decomposition (FAEMD2D)"
|
|
65
|
-
)
|
|
63
|
+
return "Bidimensional Fast and Adaptive Empirical Mode Decomposition (FAEMD2D)"
|
|
66
64
|
|
|
67
65
|
def __call__(
|
|
68
66
|
self, signal: np.ndarray, max_imfs: Optional[int] = None
|
|
@@ -81,9 +79,7 @@ class FAEMD2D(object):
|
|
|
81
79
|
return x[np.newaxis, ...], True
|
|
82
80
|
if x.ndim == 3:
|
|
83
81
|
return x, False
|
|
84
|
-
raise ValueError(
|
|
85
|
-
"FAEMD2D expects shape (H, W) or (n_channels, H, W)"
|
|
86
|
-
)
|
|
82
|
+
raise ValueError("FAEMD2D expects shape (H, W) or (n_channels, H, W)")
|
|
87
83
|
|
|
88
84
|
def fit_transform(
|
|
89
85
|
self,
|
|
@@ -58,9 +58,7 @@ class FAEMD3D(object):
|
|
|
58
58
|
self.residue: Optional[np.ndarray] = None
|
|
59
59
|
|
|
60
60
|
def __str__(self) -> str:
|
|
61
|
-
return (
|
|
62
|
-
"Tridimensional Fast and Adaptive Empirical Mode Decomposition (FAEMD3D)"
|
|
63
|
-
)
|
|
61
|
+
return "Tridimensional Fast and Adaptive Empirical Mode Decomposition (FAEMD3D)"
|
|
64
62
|
|
|
65
63
|
def __call__(
|
|
66
64
|
self, signal: np.ndarray, max_imfs: Optional[int] = None
|
|
@@ -79,9 +77,7 @@ class FAEMD3D(object):
|
|
|
79
77
|
if min(x.shape[1:]) < 3:
|
|
80
78
|
raise ValueError("Each spatial dimension must be >= 3")
|
|
81
79
|
return x, False
|
|
82
|
-
raise ValueError(
|
|
83
|
-
"FAEMD3D expects shape (X, Y, Z) or (n_channels, X, Y, Z)"
|
|
84
|
-
)
|
|
80
|
+
raise ValueError("FAEMD3D expects shape (X, Y, Z) or (n_channels, X, Y, Z)")
|
|
85
81
|
|
|
86
82
|
def fit_transform(
|
|
87
83
|
self,
|