DASPy-toolbox 1.0.0__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.
- DASPy_toolbox-1.0.0.dist-info/LICENSE.txt +1 -0
- DASPy_toolbox-1.0.0.dist-info/METADATA +85 -0
- DASPy_toolbox-1.0.0.dist-info/RECORD +49 -0
- DASPy_toolbox-1.0.0.dist-info/WHEEL +5 -0
- DASPy_toolbox-1.0.0.dist-info/entry_points.txt +2 -0
- DASPy_toolbox-1.0.0.dist-info/top_level.txt +1 -0
- daspy/__init__.py +4 -0
- daspy/advanced_tools/__init__.py +0 -0
- daspy/advanced_tools/channel.py +354 -0
- daspy/advanced_tools/decomposition.py +165 -0
- daspy/advanced_tools/denoising.py +276 -0
- daspy/advanced_tools/fdct.py +789 -0
- daspy/advanced_tools/strain2vel.py +245 -0
- daspy/basic_tools/__init__.py +0 -0
- daspy/basic_tools/filter.py +257 -0
- daspy/basic_tools/freqattributes.py +117 -0
- daspy/basic_tools/preprocessing.py +238 -0
- daspy/basic_tools/visualization.py +186 -0
- daspy/core/__init__.py +4 -0
- daspy/core/collection.py +279 -0
- daspy/core/dasdatetime.py +72 -0
- daspy/core/example.pkl +0 -0
- daspy/core/make_example.py +32 -0
- daspy/core/read.py +544 -0
- daspy/core/section.py +1319 -0
- daspy/core/write.py +282 -0
- daspy/seismic_detection/__init__.py +1 -0
- daspy/seismic_detection/calc_travel_time.py +23 -0
- daspy/seismic_detection/core.py +119 -0
- daspy/seismic_detection/detection.py +12 -0
- daspy/seismic_detection/gamma/__init__.py +13 -0
- daspy/seismic_detection/gamma/_base.py +549 -0
- daspy/seismic_detection/gamma/_bayesian_mixture.py +875 -0
- daspy/seismic_detection/gamma/_gaussian_mixture.py +866 -0
- daspy/seismic_detection/gamma/app.py +192 -0
- daspy/seismic_detection/gamma/seismic_ops.py +478 -0
- daspy/seismic_detection/gamma/utils.py +512 -0
- daspy/seismic_detection/location.py +266 -0
- daspy/seismic_detection/magnitude.py +43 -0
- daspy/seismic_detection/phase_picking.py +67 -0
- daspy/structure_imaging/__init__.py +0 -0
- daspy/structure_imaging/ambient_noise.py +4 -0
- daspy/structure_imaging/dispersion.py +27 -0
- daspy/structure_imaging/fault_zone.py +59 -0
- daspy/structure_imaging/inversion.py +6 -0
- daspy/traffic_monitoring/JamDetection.py +6 -0
- daspy/traffic_monitoring/SpeedMeasurement.py +6 -0
- daspy/traffic_monitoring/VehicleDetection.py +6 -0
- daspy/traffic_monitoring/__init__.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
daspy/LICENSE.txt
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: DASPy-toolbox
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing, which comprises classic seismic data processing techniques and Specialized algorithms for DAS applications.
|
|
5
|
+
Home-page: https://github.com/HMZ-03/DASPy
|
|
6
|
+
Author: Minzhe Hu, Zefeng Li
|
|
7
|
+
Author-email: hmz2018@mail.ustc.edu.cn
|
|
8
|
+
Maintainer: Minzhe Hu
|
|
9
|
+
Maintainer-email: hmz2018@mail.ustc.edu.cn
|
|
10
|
+
License: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
License-File: LICENSE.txt
|
|
16
|
+
Requires-Dist: numpy
|
|
17
|
+
Requires-Dist: scipy>=1.13
|
|
18
|
+
Requires-Dist: matplotlib
|
|
19
|
+
Requires-Dist: geographiclib
|
|
20
|
+
Requires-Dist: pyproj
|
|
21
|
+
Requires-Dist: h5py
|
|
22
|
+
Requires-Dist: segyio
|
|
23
|
+
Requires-Dist: nptdms
|
|
24
|
+
Requires-Dist: tqdm
|
|
25
|
+
|
|
26
|
+
<img src="./website/USTC.svg" height="170" /> <img src="./website/DAMS.png" height="150" />
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## DASPy
|
|
30
|
+
|
|
31
|
+
DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing.
|
|
32
|
+
|
|
33
|
+
The goal of the DASPy project is to lower the bar of DAS data processing. DASPy includes:
|
|
34
|
+
* Classic seismic data processing techniques, including preprocessing, filter, spectrum analysis, and visualization
|
|
35
|
+
* Specialized algorithms for DAS applications, including denoising, waveform decomposition, channel attribute analysis, and strain-velocity conversion.
|
|
36
|
+
|
|
37
|
+
DASPy is licensed under the MIT License. [An English version of DASPy tutorial](https://daspy-tutorial.readthedocs.io/en/latest/), [a Chinese version of DASPy tutorial](https://daspy-tutorial-cn.readthedocs.io/zh-cn/latest/) and [the DASPy paper](document/srl-2024124.1.pdf) is available. If you have any questions, please contact me via <hmz2018@mail.ustc.edu.cn>.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
DASPy is currently running on Linux, Windows and Mac OS.
|
|
41
|
+
DASPy runs on Python 3.9 and up. We recommend you use the latest version of python 3 if possible.
|
|
42
|
+
|
|
43
|
+
### Pip (recommanded)
|
|
44
|
+
```
|
|
45
|
+
pip install git+https://github.com/HMZ-03/DASPy.git
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If you installed DASPy this way, you can upgrade DASPy with the following command:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
pip install --upgrade git+https://github.com/HMZ-03/DASPy.git
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Conda
|
|
55
|
+
```
|
|
56
|
+
conda install -c hmz-03 daspy
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If an error is reported, please try updating conda:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
conda update -n base -c conda-forge conda
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Manual installation
|
|
66
|
+
1. Install dependent packages: numpy, scipy >=1.13, matplotlib, geographiclib, pyproj, h5py, segyio, nptdms, tqdm
|
|
67
|
+
|
|
68
|
+
2. Add DASPy into your Python path.
|
|
69
|
+
|
|
70
|
+
## Getting started
|
|
71
|
+
```
|
|
72
|
+
from daspy import read
|
|
73
|
+
sec = read() # load example waveform
|
|
74
|
+
sec.bandpass(1, 15)
|
|
75
|
+
sec.plot()
|
|
76
|
+
```
|
|
77
|
+
<img src="./website/waveform.png" height="500" />
|
|
78
|
+
|
|
79
|
+
### Contributing
|
|
80
|
+
|
|
81
|
+
Please see details on how to contribute to the project [here](CONTRIBUTING.md) and [here](CodingStyleGuide.md).
|
|
82
|
+
|
|
83
|
+
### Reference
|
|
84
|
+
|
|
85
|
+
* Minzhe Hu and Zefeng Li (2024), [DASPy: A Python Toolbox for DAS Seismology](https://pubs.geoscienceworld.org/ssa/srl/article/95/5/3055/645865/DASPy-A-Python-Toolbox-for-DAS-Seismology), *Seismological Research Letters*, 95(5), 3055–3066, doi: `https://doi.org/10.1785/0220240124`.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
daspy/__init__.py,sha256=9ePx1cKAX5SdVk9lCSeu4AjhhzimfIbbwdoeVcosUok,178
|
|
2
|
+
daspy/advanced_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
daspy/advanced_tools/channel.py,sha256=Ymw7B_ZN185_kVhZDFvQHXwycOlvyxQbRB7QOV6qzRU,13903
|
|
4
|
+
daspy/advanced_tools/decomposition.py,sha256=jAth4_xKGaFpemzpci25D4XyZUx0Nl7eGMATBouKmiA,7252
|
|
5
|
+
daspy/advanced_tools/denoising.py,sha256=qpQnuv2JUB7b6hI8MvUHAy0NqFCC0-00uYxd1xhnz2Q,10790
|
|
6
|
+
daspy/advanced_tools/fdct.py,sha256=sCZtBdSFdwcsPsjIPUmHNqUfEUK6GFEp7qVQyVRhzkc,37047
|
|
7
|
+
daspy/advanced_tools/strain2vel.py,sha256=UJpDTqoi8frtBtuxqTbQoiOd9XSHVvNuJ1Gx3eK2Uhg,9936
|
|
8
|
+
daspy/basic_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
daspy/basic_tools/filter.py,sha256=OqfAvSTZWMcqf4gve3-WvKpN_tsmjsz9azBacGpIzvI,10192
|
|
10
|
+
daspy/basic_tools/freqattributes.py,sha256=6Tb_xZ45NU0Kh-eRgzOWPA69oLjDXL7GUbMoKkr1BX4,4397
|
|
11
|
+
daspy/basic_tools/preprocessing.py,sha256=z-YpFWQxtBFrCKqWpxKzaZete0phb3fXzMemQ9SL68E,7175
|
|
12
|
+
daspy/basic_tools/visualization.py,sha256=UCRdCy5MWsBKcg0ELuDPqahCa_vjDWp1rugE-BUgRhY,7568
|
|
13
|
+
daspy/core/__init__.py,sha256=9ePx1cKAX5SdVk9lCSeu4AjhhzimfIbbwdoeVcosUok,178
|
|
14
|
+
daspy/core/collection.py,sha256=kzTR7rQtS15ShfLTsJXh9Jt4bIPJA_nsUG7apxTrJfU,11449
|
|
15
|
+
daspy/core/dasdatetime.py,sha256=ASkPjx5mF0VL4GTrCbKm08NaZsSD_2zn4sHad3yRLaQ,2097
|
|
16
|
+
daspy/core/example.pkl,sha256=cgna7XFux5gSO93y_9GSVUNJa2PKABxD68a_HenzoHc,20000481
|
|
17
|
+
daspy/core/make_example.py,sha256=pfUTZXTzHUIV2bvB197uyQNILXfv6eIACSw3Omt__9Q,994
|
|
18
|
+
daspy/core/read.py,sha256=716YcfMPMaxX4FtkOXNrxjIemup2IiMRkeufl56LXl8,23499
|
|
19
|
+
daspy/core/section.py,sha256=d5Q5x6iwxpOqKFfoi1OgCNd7T1rVZWh_JTzj7em7is8,57401
|
|
20
|
+
daspy/core/write.py,sha256=yZwsuQSJZutBk4gjuCJTm4nAph3VZtTJUYml1lVXHQM,12581
|
|
21
|
+
daspy/seismic_detection/__init__.py,sha256=CwmJ6t6tUu19F15MUAa3A0m-gdevAsbK-XDtlYk_4sw,54
|
|
22
|
+
daspy/seismic_detection/calc_travel_time.py,sha256=DnIQvqACGu35PNwDOeZ1e_aDOi4kIRPbuYU93k-X3nI,716
|
|
23
|
+
daspy/seismic_detection/core.py,sha256=SEReXzTDw3lZkz_IYoBKe8WMRJnOeBM5FxZxRfCWC7c,4817
|
|
24
|
+
daspy/seismic_detection/detection.py,sha256=QNOMIaw_qsxnGZxWEYYUXMEK_NJUSubpiRQEeQK3Pl8,180
|
|
25
|
+
daspy/seismic_detection/location.py,sha256=RVHym2hYOCiohKv6BpJKGYYLGX4j3m5XCcO9gtSIhPU,10375
|
|
26
|
+
daspy/seismic_detection/magnitude.py,sha256=G2FpTtVrxzQ_yaiycMut8R1Cq9gErES4QmWtP9Mzv3c,1181
|
|
27
|
+
daspy/seismic_detection/phase_picking.py,sha256=ggtIT4DXE0S1wHecitNpsnN2xXuUnHXp7X_paxUqy4U,1897
|
|
28
|
+
daspy/seismic_detection/gamma/__init__.py,sha256=fBgNjzcOqyAnA3-oioCzgRxOlKPq_q_QlBl3gmcB2Xw,330
|
|
29
|
+
daspy/seismic_detection/gamma/_base.py,sha256=0MJNNBwImK9AhMhqrABRuHmn1UTZn0mSNJcWNMvgLc4,19235
|
|
30
|
+
daspy/seismic_detection/gamma/_bayesian_mixture.py,sha256=6Fm8d1kYAUj_ZDYGMOQwN_Uvl5-HEniHbQRRE7-nz0w,37508
|
|
31
|
+
daspy/seismic_detection/gamma/_gaussian_mixture.py,sha256=OoeHH6P9Fe0ggaq4X7mUIRThz4dU7RndhaAKB-QYLjw,33646
|
|
32
|
+
daspy/seismic_detection/gamma/app.py,sha256=6udxd6zjU_WOpz6-wQB6zLGR5ExNwkONdSee8dToh60,7104
|
|
33
|
+
daspy/seismic_detection/gamma/seismic_ops.py,sha256=uhqozSdv9zmq6PwMRnEQqOqVXRIroQh7xOQfpZOJaWo,16106
|
|
34
|
+
daspy/seismic_detection/gamma/utils.py,sha256=N4bgksgEMQFIs99VPYKQEJIHpSwsrmqcR-l-LaqIans,20208
|
|
35
|
+
daspy/structure_imaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
+
daspy/structure_imaging/ambient_noise.py,sha256=S245_h3JeNVN7WyQLnL3ECXPVGZJXfRo28tBO3RcRww,38
|
|
37
|
+
daspy/structure_imaging/dispersion.py,sha256=G7ELTg1tbcfRHIuLzaFiPW0bYQ64qvy-U7pY4R8VhVI,827
|
|
38
|
+
daspy/structure_imaging/fault_zone.py,sha256=yWa-1zAq-Ay0Y8FQGQ_nd-Nf1r-MfWMEuuXwHOY7fm4,1872
|
|
39
|
+
daspy/structure_imaging/inversion.py,sha256=gmaNdqOfLcFiMhZQolXE6M9yJ6jzi_SKKN1KjDX31bA,48
|
|
40
|
+
daspy/traffic_monitoring/JamDetection.py,sha256=gmaNdqOfLcFiMhZQolXE6M9yJ6jzi_SKKN1KjDX31bA,48
|
|
41
|
+
daspy/traffic_monitoring/SpeedMeasurement.py,sha256=gmaNdqOfLcFiMhZQolXE6M9yJ6jzi_SKKN1KjDX31bA,48
|
|
42
|
+
daspy/traffic_monitoring/VehicleDetection.py,sha256=gmaNdqOfLcFiMhZQolXE6M9yJ6jzi_SKKN1KjDX31bA,48
|
|
43
|
+
daspy/traffic_monitoring/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
|
+
DASPy_toolbox-1.0.0.dist-info/LICENSE.txt,sha256=JQFMMufl9iR-zv6KUSm1HRskwVGj1rLUYaJt_xSYdHU,18
|
|
45
|
+
DASPy_toolbox-1.0.0.dist-info/METADATA,sha256=r62scxU1N8OeNK6dwBUAVcS5wG80bri0JiMBtAo4McA,3208
|
|
46
|
+
DASPy_toolbox-1.0.0.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
|
47
|
+
DASPy_toolbox-1.0.0.dist-info/entry_points.txt,sha256=h-kZi48_NB9-2Y985HJ_ZTy343UC8msj-sfqgs8dszs,42
|
|
48
|
+
DASPy_toolbox-1.0.0.dist-info/top_level.txt,sha256=GDX6JX11FYMlTT1iflQkX_daWBA7Vn83fOuOeMXqshM,6
|
|
49
|
+
DASPy_toolbox-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
daspy
|
daspy/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# Purpose: Several functions for analysis data quality and geometry of channels
|
|
2
|
+
# Author: Minzhe Hu, Zefeng Li
|
|
3
|
+
# Date: 2024.10.11
|
|
4
|
+
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
|
+
import numpy as np
|
|
6
|
+
from copy import deepcopy
|
|
7
|
+
from geographiclib.geodesic import Geodesic
|
|
8
|
+
from pyproj import Proj
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def robust_polyfit(data, deg, thresh):
|
|
12
|
+
"""
|
|
13
|
+
Fit a curve with a robust weighted polynomial.
|
|
14
|
+
|
|
15
|
+
:param data: 1-dimensional array.
|
|
16
|
+
:param deg: int. Degree of the fitting polynomial
|
|
17
|
+
:param thresh: int or float. Defined MAD multiple of outliers.
|
|
18
|
+
:return: Fitting data
|
|
19
|
+
"""
|
|
20
|
+
nch = len(data)
|
|
21
|
+
channels = np.arange(nch)
|
|
22
|
+
p_coef = np.polyfit(channels, data, deg)
|
|
23
|
+
p_fit = np.poly1d(p_coef)
|
|
24
|
+
old_data = p_fit(channels)
|
|
25
|
+
mse = 1
|
|
26
|
+
|
|
27
|
+
# robust fitting until the fitting curve changes < 0.1% at every point.
|
|
28
|
+
while mse > 0.001:
|
|
29
|
+
rsl = abs(data - old_data)
|
|
30
|
+
mad = np.median(rsl)
|
|
31
|
+
weights = np.zeros(nch)
|
|
32
|
+
weights[rsl < thresh * mad] = 1
|
|
33
|
+
p_coef = np.polyfit(channels, data, deg, w=weights)
|
|
34
|
+
p_fit = np.poly1d(p_coef)
|
|
35
|
+
new_data = p_fit(channels)
|
|
36
|
+
mse = np.nanmax(np.abs((new_data - old_data) / old_data))
|
|
37
|
+
old_data = new_data
|
|
38
|
+
|
|
39
|
+
return new_data, weights
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _continuity_checking(lst1, lst2, adjacent=2, toleration=2):
|
|
43
|
+
lst1_raw = deepcopy(lst1)
|
|
44
|
+
for chn in lst1_raw:
|
|
45
|
+
discont = [a for a in lst2 if abs(a - chn) <= adjacent]
|
|
46
|
+
if len(discont) >= adjacent * 2 + 1 - toleration:
|
|
47
|
+
lst1.remove(chn)
|
|
48
|
+
lst2.append(chn)
|
|
49
|
+
|
|
50
|
+
return lst1, lst2
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def channel_checking(data, deg=10, thresh=5, continuity=True, adjacent=2,
|
|
54
|
+
toleration=2, mode='low', verbose=False):
|
|
55
|
+
"""
|
|
56
|
+
Use the energy of each channel to determine which channels are bad.
|
|
57
|
+
|
|
58
|
+
:param data: 2-dimensional np.ndarray. Axis 0 is channel number and axis 1 is
|
|
59
|
+
time series
|
|
60
|
+
:param deg: int. Degree of the fitting polynomial
|
|
61
|
+
:param thresh: int or float. The MAD multiple of bad channel energy lower
|
|
62
|
+
than good channels.
|
|
63
|
+
:param continuity: bool. Perform continuity checks on bad channels and good
|
|
64
|
+
channels.
|
|
65
|
+
:param adjacent: int. The number of nearby channels for continuity checks.
|
|
66
|
+
:param toleration: int. The number of discontinuous channel allowed in each
|
|
67
|
+
channel (including itself) in the continuity check.
|
|
68
|
+
:param mode: str. 'low' means bad channels have low amplitude, 'high' means
|
|
69
|
+
bad channels have high amplitude, and 'both' means bad channels are
|
|
70
|
+
likely to have low or high amplitude.
|
|
71
|
+
:return: Good channels and bad channels.
|
|
72
|
+
"""
|
|
73
|
+
nch = len(data)
|
|
74
|
+
energy = np.log10(np.sum(data**2, axis=1))
|
|
75
|
+
|
|
76
|
+
# Remove abnormal value by robust polynomial fitting.
|
|
77
|
+
fitted_energy, weights = robust_polyfit(energy, deg, thresh)
|
|
78
|
+
deviation = energy - fitted_energy
|
|
79
|
+
|
|
80
|
+
# Iterate eliminates outliers.
|
|
81
|
+
mad = np.median(abs(deviation[weights > 0]))
|
|
82
|
+
if mode == 'low':
|
|
83
|
+
bad_chn = np.argwhere(deviation < -thresh * mad).ravel().tolist()
|
|
84
|
+
elif mode == 'high':
|
|
85
|
+
bad_chn = np.argwhere(deviation > thresh * mad).ravel().tolist()
|
|
86
|
+
elif mode == 'high':
|
|
87
|
+
bad_chn = np.argwhere(deviation < -thresh * mad).ravel().tolist() + \
|
|
88
|
+
np.argwhere(deviation > thresh * mad).ravel().tolist()
|
|
89
|
+
good_chn = list(set(range(nch)) - set(bad_chn))
|
|
90
|
+
|
|
91
|
+
if continuity:
|
|
92
|
+
# Discontinuous normal value are part of bad channels.
|
|
93
|
+
good_chn, bad_chn = _continuity_checking(good_chn, bad_chn,
|
|
94
|
+
adjacent=adjacent,
|
|
95
|
+
toleration=toleration)
|
|
96
|
+
|
|
97
|
+
# Discontinuous outliers are usually not bad channels.
|
|
98
|
+
bad_chn, good_chn = _continuity_checking(bad_chn, good_chn,
|
|
99
|
+
adjacent=adjacent,
|
|
100
|
+
toleration=toleration)
|
|
101
|
+
|
|
102
|
+
bad_chn = np.sort(np.array(bad_chn))
|
|
103
|
+
good_chn = np.sort(np.array(good_chn))
|
|
104
|
+
if verbose:
|
|
105
|
+
return good_chn, bad_chn, energy, fitted_energy - thresh * mad
|
|
106
|
+
|
|
107
|
+
return good_chn, bad_chn
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _channel_location(track_pt):
|
|
111
|
+
track, tn = track_pt[:, :-1], track_pt[:, -1]
|
|
112
|
+
dim = track.shape[1]
|
|
113
|
+
l_track = np.sqrt(np.sum(np.diff(track, axis=0) ** 2, axis=1))
|
|
114
|
+
l_track_cum = np.hstack(([0], np.cumsum(l_track)))
|
|
115
|
+
idx_kp = np.where(tn >= 0)[0]
|
|
116
|
+
|
|
117
|
+
interp_ch = []
|
|
118
|
+
chn = np.floor(tn[idx_kp[0]])
|
|
119
|
+
if abs(chn - tn[idx_kp[0]]) < 1e-6:
|
|
120
|
+
interp_ch.append([*track[idx_kp[0]], chn])
|
|
121
|
+
|
|
122
|
+
seg_interval = []
|
|
123
|
+
for i in range(1, len(idx_kp)):
|
|
124
|
+
# calculate actual interval between known-channel points
|
|
125
|
+
istart, iend = idx_kp[i - 1], idx_kp[i]
|
|
126
|
+
n_chn_kp = tn[iend] - tn[istart]
|
|
127
|
+
d_interp = (l_track_cum[iend] - l_track_cum[istart]) / n_chn_kp
|
|
128
|
+
seg_interval.append([tn[istart], tn[iend], d_interp])
|
|
129
|
+
|
|
130
|
+
l_res = 0 # remaining fiber length before counting the next segment
|
|
131
|
+
# consider if the given channelnumber is not an integer
|
|
132
|
+
chn_res = tn[istart] - int(tn[istart])
|
|
133
|
+
for j in range(istart, iend):
|
|
134
|
+
l_start = l_track[j] + l_res
|
|
135
|
+
|
|
136
|
+
# if tp segment length is large for more than one interval, get the
|
|
137
|
+
# channel loc
|
|
138
|
+
if l_start >= d_interp * (1 - chn_res):
|
|
139
|
+
# floor int, num of channel available
|
|
140
|
+
n_chn_tp = int(l_start / d_interp + chn_res)
|
|
141
|
+
l_new = (np.arange(n_chn_tp) + 1 - chn_res) * d_interp - \
|
|
142
|
+
l_res # channel distance from segment start
|
|
143
|
+
|
|
144
|
+
# interpolate the channel loc
|
|
145
|
+
t_new = np.zeros((len(l_new), dim))
|
|
146
|
+
for d in range(dim):
|
|
147
|
+
t_new[:, d] = np.interp(l_new, [0, l_track[j]],
|
|
148
|
+
[track[j, d], track[j + 1, d]])
|
|
149
|
+
|
|
150
|
+
# remaining length to add to next segment
|
|
151
|
+
l_res = l_start - n_chn_tp * d_interp
|
|
152
|
+
|
|
153
|
+
# write interpolated channel loc
|
|
154
|
+
for ti in t_new:
|
|
155
|
+
chn += 1
|
|
156
|
+
interp_ch.append([*ti, chn])
|
|
157
|
+
|
|
158
|
+
# handle floor int problem when l_start/d_interp is near an
|
|
159
|
+
# interger
|
|
160
|
+
if (d_interp - l_res) / d_interp < 1e-6:
|
|
161
|
+
chn += 1
|
|
162
|
+
interp_ch.append([*track[j + 1, :], int(tn[j + 1])])
|
|
163
|
+
l_res = 0
|
|
164
|
+
chn_res = 0
|
|
165
|
+
# if tp segment length is not enough for one interval, simply add
|
|
166
|
+
# the length to next segment
|
|
167
|
+
elif l_start < d_interp:
|
|
168
|
+
l_res = l_start
|
|
169
|
+
|
|
170
|
+
return np.array(seg_interval), np.array(interp_ch)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def location_interpolation(known_pt, track_pt=None, dx=2, data_type='lonlat',
|
|
174
|
+
verbose=False):
|
|
175
|
+
"""
|
|
176
|
+
Interpolate to obtain the positions of all channels.
|
|
177
|
+
|
|
178
|
+
:param known_pt: np.ndarray. Points with known channel numbers. Each row
|
|
179
|
+
includes 2 or 3 coordinates and a channel number.
|
|
180
|
+
:param track_pt: np.ndarray. Optional fiber spatial track points without
|
|
181
|
+
channel numbers. Each row includes 2 or 3 coordinates. Please ensure
|
|
182
|
+
that the track points are arranged in increasing order of track number.
|
|
183
|
+
If track points is not dense enough, please insert the coordinates of
|
|
184
|
+
known points into track points in order.
|
|
185
|
+
:param dx: Known points far from the track (> dx) will be excluded.
|
|
186
|
+
Recommended setting is channel interval. The unit is m.
|
|
187
|
+
:param data_type: str. Coordinate type. 'lonlat' ('lonlatheight') for
|
|
188
|
+
longitude, latitude in degree (and height in meters), 'xy' ('xyz') for
|
|
189
|
+
x, y (and z) in meters.
|
|
190
|
+
:param verbose: bool. If True, return interpoleted channel location and
|
|
191
|
+
segment interval.
|
|
192
|
+
:return: Interpoleted channel location if verbose is False.
|
|
193
|
+
"""
|
|
194
|
+
known_pt = known_pt[known_pt[:,-1].argsort()]
|
|
195
|
+
dim = known_pt.shape[1] - 1
|
|
196
|
+
if 'lonlat' in data_type:
|
|
197
|
+
zone = np.floor((max(known_pt[:,0]) + min(known_pt[:,0])) / 2 / 6)\
|
|
198
|
+
.astype(int) + 31
|
|
199
|
+
DASProj = Proj(proj='utm', zone=zone, ellps='WGS84',
|
|
200
|
+
preserve_units=False)
|
|
201
|
+
known_pt[:, 0], known_pt[:, 1] = DASProj(known_pt[:, 0], known_pt[:, 1])
|
|
202
|
+
else:
|
|
203
|
+
assert 'xy' in data_type, ('data_type should be \'lonlat\',\''
|
|
204
|
+
'lonlatheight\', \'xy\' or \'xyz\'')
|
|
205
|
+
|
|
206
|
+
if track_pt is None:
|
|
207
|
+
seg_interval, interp_ch = _channel_location(known_pt)
|
|
208
|
+
else:
|
|
209
|
+
K = len(known_pt)
|
|
210
|
+
T = len(track_pt)
|
|
211
|
+
track_pt = np.c_[track_pt, np.zeros(T) - 1]
|
|
212
|
+
if 'lonlat' in data_type:
|
|
213
|
+
track_pt[:, 0], track_pt[:, 1] = DASProj(track_pt[:, 0],
|
|
214
|
+
track_pt[:, 1])
|
|
215
|
+
|
|
216
|
+
# insert the known points into the fiber track data
|
|
217
|
+
matrix = [np.tile(track_pt[:, d], (K, 1)) -
|
|
218
|
+
np.tile(known_pt[:, d], (T, 1)).T for d in range(dim)]
|
|
219
|
+
|
|
220
|
+
dist = np.sqrt(np.sum(np.array(matrix) ** 2, axis=0))
|
|
221
|
+
for k in range(K):
|
|
222
|
+
if min(dist[k]) < dx:
|
|
223
|
+
t_list = np.sort(np.where(dist[k] == min(dist[k]))[0])
|
|
224
|
+
for t in t_list:
|
|
225
|
+
if track_pt[t, -1] == -1:
|
|
226
|
+
track_pt[t, -1] = known_pt[k, -1]
|
|
227
|
+
last_pt = t
|
|
228
|
+
break
|
|
229
|
+
|
|
230
|
+
# interpolation with regular spacing along the fiber track
|
|
231
|
+
try:
|
|
232
|
+
track_pt = track_pt[:last_pt + 1]
|
|
233
|
+
except NameError:
|
|
234
|
+
print('All known points are too far away from the track points. If '
|
|
235
|
+
'they are reliable, they can be merged in sequence as track '
|
|
236
|
+
'points to input')
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
seg_interval, interp_ch = _channel_location(track_pt)
|
|
240
|
+
|
|
241
|
+
if data_type == 'lonlat':
|
|
242
|
+
interp_ch[:, 0], interp_ch[:, 1] = \
|
|
243
|
+
DASProj(interp_ch[:, 0], interp_ch[:, 1], inverse=True)
|
|
244
|
+
|
|
245
|
+
if verbose:
|
|
246
|
+
return interp_ch, seg_interval
|
|
247
|
+
return interp_ch
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _xcorr(x, y):
|
|
251
|
+
N = len(x)
|
|
252
|
+
meanx = np.mean(x)
|
|
253
|
+
meany = np.mean(y)
|
|
254
|
+
stdx = np.std(np.asarray(x))
|
|
255
|
+
stdy = np.std(np.asarray(y))
|
|
256
|
+
c = np.sum((y - meany) * (x - meanx)) / (N * stdx * stdy)
|
|
257
|
+
return c
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _horizontal_angle_change(geo, gap=10):
|
|
261
|
+
nch = len(geo)
|
|
262
|
+
angle = np.zeros(nch)
|
|
263
|
+
for i in range(1, nch - 1):
|
|
264
|
+
lon, lat = geo[i]
|
|
265
|
+
lon_s, lat_s = geo[max(i - gap, 0)]
|
|
266
|
+
lon_e, lat_e = geo[min(i + gap, nch - 1)]
|
|
267
|
+
azi_s = Geodesic.WGS84.Inverse(lat_s, lon_s, lat, lon)['azi1']
|
|
268
|
+
azi_e = Geodesic.WGS84.Inverse(lat, lon, lat_e, lon_e)['azi1']
|
|
269
|
+
dazi = azi_e - azi_s
|
|
270
|
+
if abs(dazi) > 180:
|
|
271
|
+
dazi = -np.sign(dazi) * (360 - abs(dazi))
|
|
272
|
+
angle[i] = dazi
|
|
273
|
+
|
|
274
|
+
return angle
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _vertical_angle_change(geo, gap=10):
|
|
278
|
+
nch = len(geo)
|
|
279
|
+
angle = np.zeros(nch)
|
|
280
|
+
for i in range(1, nch - 1):
|
|
281
|
+
lon, lat, dep = geo[i]
|
|
282
|
+
lon_s, lat_s, dep_s = geo[max(i - gap, 0)]
|
|
283
|
+
lon_e, lat_e, dep_e = geo[min(i + gap, nch - 1)]
|
|
284
|
+
s12_s = Geodesic.WGS84.Inverse(lat_s, lon_s, lat, lon)['s12']
|
|
285
|
+
theta_s = np.arctan((dep - dep_s) / s12_s) / np.pi * 180
|
|
286
|
+
s12_e = Geodesic.WGS84.Inverse(lat, lon, lat_e, lon_e)['s12']
|
|
287
|
+
theta_e = np.arctan((dep_e - dep) / s12_e) / np.pi * 180
|
|
288
|
+
angle[i] = theta_e - theta_s
|
|
289
|
+
|
|
290
|
+
return angle
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _local_maximum_indexes(data, thresh):
|
|
294
|
+
idx = np.where(data > thresh)[0]
|
|
295
|
+
if len(idx):
|
|
296
|
+
i = list(np.where(np.diff(idx) > 1)[0] + 1)
|
|
297
|
+
if len(idx) - 1 not in i:
|
|
298
|
+
i.append(len(idx) - 1)
|
|
299
|
+
b = 0
|
|
300
|
+
max_idx = []
|
|
301
|
+
for e in i:
|
|
302
|
+
max_idx.append(idx[b] + np.argmax(data[idx[b]:idx[e]]))
|
|
303
|
+
b = e
|
|
304
|
+
return max_idx
|
|
305
|
+
else:
|
|
306
|
+
return []
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def turning_points(data, data_type='coordinate', thresh=5, depth_info=False,
|
|
310
|
+
channel_gap=3):
|
|
311
|
+
"""
|
|
312
|
+
Seek turning points in the DAS channel.
|
|
313
|
+
|
|
314
|
+
:param data: numpy.ndarray. Data used to seek turning points.
|
|
315
|
+
:param data_type: str. If data_type is 'coordinate', data should include
|
|
316
|
+
longitude and latitude (first two columns), and can also include depth
|
|
317
|
+
(last column). If data_type is 'waveform', data should be continuous
|
|
318
|
+
waveform, preferably containing signal with strong coherence
|
|
319
|
+
(earthquake, traffic signal, etc.).
|
|
320
|
+
:param thresh: For coordinate data, when the angle of the optical cables on
|
|
321
|
+
both sides centered on a certain point exceeds thresh, it is considered
|
|
322
|
+
an turning point. For waveform, thresh means the MAD multiple of
|
|
323
|
+
adjacent channel cross-correlation values lower than their median.
|
|
324
|
+
:param depth_info: bool. Optional if data_type is 'coordinate'. Whether
|
|
325
|
+
depth (in meters) is included in the coordinate data and need to be
|
|
326
|
+
used.
|
|
327
|
+
:param channel_gap: int. Optional if data_type is 'coordinate'. The smaller
|
|
328
|
+
the value is, the finer the segmentation will be. It is recommended to
|
|
329
|
+
set it to half the ratio of gauge length and channel interval.
|
|
330
|
+
:return: list. Channel index of turning points.
|
|
331
|
+
"""
|
|
332
|
+
if data_type == 'coordinate':
|
|
333
|
+
angle = _horizontal_angle_change(data[:, :2], gap=channel_gap)
|
|
334
|
+
turning_h = _local_maximum_indexes(abs(angle), thresh)
|
|
335
|
+
|
|
336
|
+
if depth_info:
|
|
337
|
+
angle = _vertical_angle_change(data, gap=channel_gap)
|
|
338
|
+
turning_v = _local_maximum_indexes(abs(angle), thresh)
|
|
339
|
+
return turning_h, turning_v
|
|
340
|
+
|
|
341
|
+
return turning_h
|
|
342
|
+
|
|
343
|
+
elif data_type == 'waveform':
|
|
344
|
+
nch = len(data)
|
|
345
|
+
cc = np.zeros(nch - 1)
|
|
346
|
+
for i in range(nch - 1):
|
|
347
|
+
cc[i] = _xcorr(data[i], data[i + 1])
|
|
348
|
+
median = np.median(cc)
|
|
349
|
+
mad = np.median(abs(cc - median))
|
|
350
|
+
|
|
351
|
+
return np.argwhere(cc < median - thresh * mad)[0]
|
|
352
|
+
|
|
353
|
+
else:
|
|
354
|
+
raise ValueError('Data_type should be \'coordinate\' or \'waveform\'.')
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Purpose: Waveform decomposition
|
|
2
|
+
# Author: Minzhe Hu
|
|
3
|
+
# Date: 2024.5.13
|
|
4
|
+
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
|
+
import numpy as np
|
|
6
|
+
from numpy.fft import irfft2, ifftshift
|
|
7
|
+
from daspy.basic_tools.preprocessing import padding, cosine_taper
|
|
8
|
+
from daspy.basic_tools.freqattributes import next_pow_2, fk_transform
|
|
9
|
+
from daspy.advanced_tools.denoising import curvelet_denoising
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def fk_fan_mask(f, k, fmin=None, fmax=None, kmin=None, kmax=None, vmin=None,
|
|
13
|
+
vmax=None, edge=0.1, flag=None):
|
|
14
|
+
"""
|
|
15
|
+
Make a fan mask in f-k domain for f-k filter.
|
|
16
|
+
|
|
17
|
+
:param f: Frequency sequence.
|
|
18
|
+
:param k: Wavenumber sequence.
|
|
19
|
+
:param fmin, fmax, kmin, kmax, vmin, vmax: float or or sequence of 2 floats.
|
|
20
|
+
Sequence of 2 floats represents the start and end of taper.
|
|
21
|
+
:param edge: float. The width of fan mask taper edge.
|
|
22
|
+
:param flag: -1 keep only negative apparent velocities, 0 keep both postive
|
|
23
|
+
and negative apparent velocities, 1 keep only positive apparent
|
|
24
|
+
velocities.
|
|
25
|
+
:return: Fan mask.
|
|
26
|
+
"""
|
|
27
|
+
ff = np.tile(f, (len(k), 1))
|
|
28
|
+
kk = np.tile(k, (len(f), 1)).T
|
|
29
|
+
vv = - np.divide(ff, kk, out=np.ones_like(ff) * 1e10, where=kk != 0)
|
|
30
|
+
mask = np.ones(vv.shape)
|
|
31
|
+
for phy_quan in ['f', 'k', 'v']:
|
|
32
|
+
p = eval(phy_quan * 2)
|
|
33
|
+
pmin = eval(phy_quan + 'min')
|
|
34
|
+
if pmin:
|
|
35
|
+
if isinstance(pmin, (tuple, list, np.ndarray)):
|
|
36
|
+
tp_b, tp_e = min(pmin), max(pmin)
|
|
37
|
+
else:
|
|
38
|
+
tp_b, tp_e = pmin * max(1 - edge / 2, 0), pmin * (1 + edge / 2)
|
|
39
|
+
tp_wid = tp_e - tp_b
|
|
40
|
+
mask[(abs(p) <= tp_b)] = 0
|
|
41
|
+
area = (abs(p) > tp_b) & (abs(p) < tp_e)
|
|
42
|
+
mask[area] *= 0.5 - 0.5 * \
|
|
43
|
+
np.cos(((abs(p[area]) - tp_b) / tp_wid) * np.pi)
|
|
44
|
+
|
|
45
|
+
pmax = eval(phy_quan + 'max')
|
|
46
|
+
if pmax:
|
|
47
|
+
if isinstance(pmax, (tuple, list, np.ndarray)):
|
|
48
|
+
tp_b, tp_e = max(pmax), min(pmax)
|
|
49
|
+
else:
|
|
50
|
+
tp_b, tp_e = pmax * (1 + edge / 2), pmax * (1 - edge / 2)
|
|
51
|
+
tp_wid = tp_b - tp_e
|
|
52
|
+
mask[(abs(p) >= tp_b)] = 0
|
|
53
|
+
area = (abs(p) > tp_e) & (abs(p) < tp_b)
|
|
54
|
+
mask[area] *= 0.5 - 0.5 * \
|
|
55
|
+
np.cos(((tp_b - abs(p[area])) / tp_wid) * np.pi)
|
|
56
|
+
|
|
57
|
+
if flag:
|
|
58
|
+
mask[np.sign(vv) == flag] = 0
|
|
59
|
+
return mask
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def fk_filter(data, dx, fs, taper=(0.02, 0.05), pad='default', mode='decompose',
|
|
63
|
+
fmin=None, fmax=None, kmin=None, kmax=None, vmin=None, vmax=None,
|
|
64
|
+
edge=0.1, flag=None, verbose=False):
|
|
65
|
+
"""
|
|
66
|
+
Transform the data to the f-k domain using 2-D Fourier transform method, and
|
|
67
|
+
transform back to the x-t domain after filtering.
|
|
68
|
+
|
|
69
|
+
:param data: numpy.ndarray. Data to do fk filter.
|
|
70
|
+
:param dx: Channel interval in m.
|
|
71
|
+
:param fs: Sampling rate in Hz.
|
|
72
|
+
:param taper: float or sequence of floats. Each float means decimal
|
|
73
|
+
percentage of Tukey taper for corresponding dimension (ranging from 0 to
|
|
74
|
+
1). Default is 0.1 which tapers 5% from the beginning and 5% from the
|
|
75
|
+
end.
|
|
76
|
+
:param pad: Pad the data or not. It can be float or sequence of floats. Each
|
|
77
|
+
float means padding percentage before FFT for corresponding dimension.
|
|
78
|
+
If set to 0.1 will pad 5% before the beginning and after the end.
|
|
79
|
+
'default' means pad both dimensions to next power of 2. None or False
|
|
80
|
+
means don't pad data before or during Fast Fourier Transform.
|
|
81
|
+
:param mode: str. 'remove' for denoising, 'retain' for extraction, and
|
|
82
|
+
'decompose' for decomposition.
|
|
83
|
+
:param fmin, fmax, kmin, kmax, vmin, vmax: float or or sequence of 2 floats.
|
|
84
|
+
Sequence of 2 floats represents the start and end of taper.
|
|
85
|
+
:param edge: float. The width of fan mask taper edge.
|
|
86
|
+
:param flag: -1 keep only negative apparent velocities, 0 keep both postive
|
|
87
|
+
and negative apparent velocities, 1 keep only positive apparent
|
|
88
|
+
velocities.
|
|
89
|
+
:param verbose: If True, return filtered data, f-k spectrum, frequency
|
|
90
|
+
sequence, wavenumber sequence and f-k mask.
|
|
91
|
+
:return: Filtered data and some variables in the process if verbose==True.
|
|
92
|
+
"""
|
|
93
|
+
data_tp = cosine_taper(data, taper)
|
|
94
|
+
if pad == 'default':
|
|
95
|
+
nch, nt = data.shape
|
|
96
|
+
dn = (next_pow_2(nch) - nch, next_pow_2(nt) - nt)
|
|
97
|
+
nfft = None
|
|
98
|
+
elif pad is None or pad is False:
|
|
99
|
+
dn = 0
|
|
100
|
+
nfft = None
|
|
101
|
+
else:
|
|
102
|
+
dn = np.round(np.array(pad) * data.shape).astype(int)
|
|
103
|
+
nfft = 'default'
|
|
104
|
+
|
|
105
|
+
data_pd = padding(data_tp, dn)
|
|
106
|
+
nch, nt = data_pd.shape
|
|
107
|
+
|
|
108
|
+
fk, f, k = fk_transform(data_pd, dx, fs, taper=0, nfft=nfft)
|
|
109
|
+
|
|
110
|
+
mask = fk_fan_mask(f, k, fmin, fmax, kmin, kmax, vmin, vmax, edge=edge,
|
|
111
|
+
flag=flag)
|
|
112
|
+
|
|
113
|
+
if mode == 'remove':
|
|
114
|
+
mask = 1 - mask
|
|
115
|
+
|
|
116
|
+
if mode == 'decompose':
|
|
117
|
+
data_flt1 = irfft2(ifftshift(fk * mask, axes=0)).real[:nch, :nt]
|
|
118
|
+
data_flt1 = padding(data_flt1, dn, reverse=True)
|
|
119
|
+
data_flt2 = irfft2(ifftshift(fk * (1 - mask), axes=0)).real[:nch, :nt]
|
|
120
|
+
data_flt2 = padding(data_flt2, dn, reverse=True)
|
|
121
|
+
if verbose:
|
|
122
|
+
return data_flt1, data_flt2, fk, f, k, mask
|
|
123
|
+
else:
|
|
124
|
+
return data_flt1, data_flt2
|
|
125
|
+
else:
|
|
126
|
+
data_flt = irfft2(ifftshift(fk * mask, axes=0)).real[:nch, :nt]
|
|
127
|
+
data_flt = padding(data_flt, dn, reverse=True)
|
|
128
|
+
if verbose:
|
|
129
|
+
return data_flt, fk, f, k, mask
|
|
130
|
+
else:
|
|
131
|
+
return data_flt
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def curvelet_windowing(data, dx, fs, mode='decompose', vmin=0, vmax=np.inf,
|
|
135
|
+
flag=None, pad=0.3, scale_begin=3, nbscales=None,
|
|
136
|
+
nbangles=16, finest=1):
|
|
137
|
+
"""
|
|
138
|
+
Use curevelet transform to keep cooherent signal with certain velocity
|
|
139
|
+
range. {Atterholt et al., 2022 , Geophys. J. Int.}
|
|
140
|
+
|
|
141
|
+
:param data: numpy.ndarray. Data to decomposite.
|
|
142
|
+
:param dx: Channel interval in m.
|
|
143
|
+
:param fs: Sampling rate in Hz.
|
|
144
|
+
:param mode: str. 'remove' for denoising, 'retain' for extraction, and
|
|
145
|
+
'decompose' for decomposition.
|
|
146
|
+
:param vmin, vmax: float. Velocity range in m/s.
|
|
147
|
+
:param flag: -1 keep only negative apparent velocities, 0 keep both postive
|
|
148
|
+
and negative apparent velocities, 1 keep only positive apparent
|
|
149
|
+
velocities.
|
|
150
|
+
:param pad: float or sequence of floats. Each float means padding percentage
|
|
151
|
+
before FFT for corresponding dimension. If set to 0.1 will pad 5% before
|
|
152
|
+
the beginning and after the end.
|
|
153
|
+
:param scale_begin: int. The beginning scale to do coherent denoising.
|
|
154
|
+
:param nbscales: int. Number of scales including the coarsest wavelet level.
|
|
155
|
+
Default set to ceil(log2(min(M,N)) - 3).
|
|
156
|
+
:param nbangles: int. Number of angles at the 2nd coarsest level,
|
|
157
|
+
minimum 8, must be a multiple of 4.
|
|
158
|
+
:param finest: int. Objects at the finest scale. 1 for curvelets, 2 for
|
|
159
|
+
wavelets. Curvelets are more precise while wavelets are more efficient.
|
|
160
|
+
:return: numpy.ndarray. Decomposed data.
|
|
161
|
+
"""
|
|
162
|
+
return curvelet_denoising(data, choice=1, pad=pad, vmin=vmin, vmax=vmax,
|
|
163
|
+
flag=flag, dx=dx, fs=fs, mode=mode,
|
|
164
|
+
scale_begin=scale_begin, nbscales=nbscales,
|
|
165
|
+
nbangles=nbangles, finest=finest)
|