Skip to content

Commit 4728ab7

Browse files
authored
Merge branch 'dev' into patch-N-dims
2 parents 6f508ee + ce20099 commit 4728ab7

24 files changed

+634
-267
lines changed

.pre-commit-config.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,12 @@ repos:
1919
hooks:
2020
- id: isort
2121
name: isort (python)
22-
args: ["--profile", "black", "--filter-files", "--line-length=88"]
22+
args:
23+
[
24+
"--profile",
25+
"black",
26+
"--skip",
27+
"__init__.py",
28+
"--filter-files",
29+
"--line-length=88",
30+
]

MIGRATION_V1_V2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ should be used as a checklist when converting a piece of code using PyLops from
77

88
- Several operators have deprecated `N` as a keyword. To migrate, pass only `dims` if both `N` and `dims` are currently being passed.
99
If only `N` is being passed, ensure it is being passed as a value and not a keyword argument (e.g., change `Flip(N=100)` to `Flip(100)`).
10+
- `utils.dottest`: The relative tolerance is new set via `rtol` (before `tol`), and absolute tolerance is new supported via the keyword `atol`. When calling it with purely positional arguments, note that after `rtol` comes now first `atol` before `complexflag`. When using `raiseerror=True` it now emits an `AttributeError` instead of a `ValueError`.

examples/plot_nmo.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def _rmatvec(self, y):
296296
# and adjoint transforms truly are adjoints of each other.
297297

298298
NMOOp = NMO(t, x, vel_t)
299-
dottest(NMOOp, *NMOOp.shape, tol=1e-4)
299+
dottest(NMOOp, *NMOOp.shape, rtol=1e-4)
300300

301301
###############################################################################
302302
# NMO using :py:class:`pylops.Spread`
@@ -370,7 +370,7 @@ def create_tables(taxis, haxis, vels_rms):
370370
dtable=nmo_dtable, # Table of weights for linear interpolation
371371
engine="numba", # numba or numpy
372372
).H # To perform NMO *correction*, we need the adjoint
373-
dottest(SpreadNMO, *SpreadNMO.shape, tol=1e-4)
373+
dottest(SpreadNMO, *SpreadNMO.shape, rtol=1e-4)
374374

375375
###############################################################################
376376
# We see it passes the dot test, but are the results right? Let's find out.

examples/plot_slopeest.py

Lines changed: 129 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,34 @@
1212
compressed and the sparse nature of the Seislet transform can also be used to
1313
precondition sparsity-promoting inverse problems.
1414
15+
We will show examples of a variety of different settings, including a comparison
16+
with the original implementation in [1].
17+
18+
.. [1] van Vliet, L. J., Verbeek, P. W., "Estimators for orientation and
19+
anisotropy in digitized images", Journal ASCI Imaging Workshop. 1995.
20+
1521
"""
1622

1723
import matplotlib.pyplot as plt
1824
import numpy as np
25+
from matplotlib.image import imread
1926
from mpl_toolkits.axes_grid1 import make_axes_locatable
2027

2128
import pylops
2229
from pylops.signalprocessing.Seislet import _predict_trace
30+
from pylops.utils.signalprocessing import slope_estimate
2331

2432
plt.close("all")
2533
np.random.seed(10)
2634

2735
###############################################################################
36+
# Python logo
37+
# -----------
2838
# To start we import a 2d image and estimate the local slopes of the image.
2939
im = np.load("../testdata/python.npy")[..., 0]
3040
im = im / 255.0 - 0.5
3141

32-
slopes, anisotropy = pylops.utils.signalprocessing.slope_estimate(im, smooth=7)
42+
slopes, anisotropy = slope_estimate(im, smooth=7)
3343
angles = -np.rad2deg(np.arctan(slopes))
3444

3545
###############################################################################
@@ -51,6 +61,8 @@
5161
fig.tight_layout()
5262

5363
###############################################################################
64+
# Seismic data
65+
# ------------
5466
# We can now repeat the same using some seismic data. We will first define
5567
# a single trace and a slope field, apply such slope field to the trace
5668
# recursively to create the other traces of the data and finally try to recover
@@ -87,7 +99,7 @@
8799
d[:, ix] = tr
88100

89101
# Estimate slopes
90-
slope_est, _ = pylops.utils.signalprocessing.slope_estimate(d, dt, dx, smooth=10)
102+
slope_est, _ = slope_estimate(d, dt, dx, smooth=10)
91103
slope_est *= -1
92104

93105
###############################################################################
@@ -123,6 +135,120 @@
123135
fig.tight_layout()
124136

125137
###############################################################################
138+
# Concentric circles
139+
# ------------------
140+
# The original paper by van Vliet and Verbeek [1] has an example with concentric
141+
# circles. We recover their original images and compare our implementation with
142+
# theirs.
143+
def rgb2gray(rgb):
144+
return np.dot(rgb[..., :3], [0.2989, 0.5870, 0.1140])
145+
146+
147+
circles_input = rgb2gray(imread("../testdata/slope_estimate/concentric.png"))
148+
circles_angles = rgb2gray(imread("../testdata/slope_estimate/concentric_angles.png"))
149+
150+
slope, anisos_sm0 = slope_estimate(circles_input, smooth=0)
151+
angles_sm0 = np.rad2deg(np.arctan(slope))
152+
153+
slope, anisos_sm4 = slope_estimate(circles_input, smooth=4)
154+
angles_sm4 = np.rad2deg(np.arctan(slope))
155+
156+
###############################################################################
157+
fig, axs = plt.subplots(2, 3, figsize=(6, 4), sharex=True, sharey=True)
158+
axs[0, 0].imshow(circles_input, cmap="gray", aspect="equal")
159+
axs[0, 0].set(title="Original Image")
160+
cax = make_axes_locatable(axs[0, 0]).append_axes("right", size="5%", pad=0.05)
161+
cax.axis("off")
162+
163+
axs[1, 0].imshow(-circles_angles, cmap="RdBu_r")
164+
axs[1, 0].set(title="Original Angles")
165+
cax = make_axes_locatable(axs[1, 0]).append_axes("right", size="5%", pad=0.05)
166+
cax.axis("off")
167+
168+
im = axs[0, 1].imshow(angles_sm0, cmap="RdBu_r", vmin=-90, vmax=90)
169+
cax = make_axes_locatable(axs[0, 1]).append_axes("right", size="5%", pad=0.05)
170+
cb = fig.colorbar(im, cax=cax, orientation="vertical")
171+
axs[0, 1].set(title="Angles (smooth=0)")
172+
173+
im = axs[1, 1].imshow(angles_sm4, cmap="RdBu_r", vmin=-90, vmax=90)
174+
cax = make_axes_locatable(axs[1, 1]).append_axes("right", size="5%", pad=0.05)
175+
cb = fig.colorbar(im, cax=cax, orientation="vertical")
176+
axs[1, 1].set(title="Angles (smooth=4)")
177+
178+
im = axs[0, 2].imshow(anisos_sm0, cmap="Reds", vmin=0, vmax=1)
179+
cax = make_axes_locatable(axs[0, 2]).append_axes("right", size="5%", pad=0.05)
180+
cb = fig.colorbar(im, cax=cax, orientation="vertical")
181+
axs[0, 2].set(title="Anisotropy (smooth=0)")
182+
183+
im = axs[1, 2].imshow(anisos_sm4, cmap="Reds", vmin=0, vmax=1)
184+
cax = make_axes_locatable(axs[1, 2]).append_axes("right", size="5%", pad=0.05)
185+
cb = fig.colorbar(im, cax=cax, orientation="vertical")
186+
axs[1, 2].set(title="Anisotropy (smooth=4)")
187+
188+
for ax in axs.ravel():
189+
ax.axis("off")
190+
fig.tight_layout()
191+
192+
193+
###############################################################################
194+
# Core samples
195+
# ------------------
196+
# The original paper by van Vliet and Verbeek [1] also has an example with images
197+
# of core samples. Since the original paper does not have a scale with which to
198+
# plot the angles, we have chosen ours it to match their image as closely as
199+
# possible.
200+
201+
core_input = rgb2gray(imread("../testdata/slope_estimate/core_sample.png"))
202+
core_angles = rgb2gray(imread("../testdata/slope_estimate/core_sample_orientation.png"))
203+
core_aniso = rgb2gray(imread("../testdata/slope_estimate/core_sample_anisotropy.png"))
204+
205+
206+
slope, anisos_sm4 = slope_estimate(core_input, smooth=4)
207+
angles_sm4 = np.rad2deg(np.arctan(slope))
208+
209+
slope, anisos_sm8 = slope_estimate(core_input, smooth=8)
210+
angles_sm8 = np.rad2deg(np.arctan(slope))
211+
212+
###############################################################################
213+
fig, axs = plt.subplots(1, 6, figsize=(10, 6))
214+
215+
axs[0].imshow(core_input, cmap="gray_r", aspect="equal")
216+
axs[0].set(title="Original\nImage")
217+
cax = make_axes_locatable(axs[0]).append_axes("right", size="20%", pad=0.05)
218+
cax.axis("off")
219+
220+
axs[1].imshow(-core_angles, cmap="YlGnBu_r")
221+
axs[1].set(title="Original\nAngles")
222+
cax = make_axes_locatable(axs[1]).append_axes("right", size="20%", pad=0.05)
223+
cax.axis("off")
224+
225+
im = axs[2].imshow(angles_sm8, cmap="YlGnBu_r", vmin=-49, vmax=-11)
226+
cax = make_axes_locatable(axs[2]).append_axes("right", size="20%", pad=0.05)
227+
cb = fig.colorbar(im, cax=cax, orientation="vertical")
228+
axs[2].set(title="Angles\n(smooth=8)")
229+
230+
im = axs[3].imshow(angles_sm4, cmap="YlGnBu_r", vmin=-49, vmax=-11)
231+
cax = make_axes_locatable(axs[3]).append_axes("right", size="20%", pad=0.05)
232+
cb = fig.colorbar(im, cax=cax, orientation="vertical")
233+
axs[3].set(title="Angles\n(smooth=4)")
234+
235+
im = axs[4].imshow(anisos_sm8, cmap="Reds", vmin=0, vmax=1)
236+
cax = make_axes_locatable(axs[4]).append_axes("right", size="20%", pad=0.05)
237+
cb = fig.colorbar(im, cax=cax, orientation="vertical")
238+
axs[4].set(title="Anisotropy\n(smooth=8)")
239+
240+
im = axs[5].imshow(anisos_sm4, cmap="Reds", vmin=0, vmax=1)
241+
cax = make_axes_locatable(axs[5]).append_axes("right", size="20%", pad=0.05)
242+
cb = fig.colorbar(im, cax=cax, orientation="vertical")
243+
axs[5].set(title="Anisotropy\n(smooth=4)")
244+
245+
for ax in axs.ravel():
246+
ax.axis("off")
247+
fig.tight_layout()
248+
249+
###############################################################################
250+
# Final considerations
251+
# --------------------
126252
# As you can see the Structure Tensor algorithm is a very fast, general purpose
127253
# algorithm that can be used to estimate local slopes to input datasets of
128-
# very different nature.
254+
# very different natures.

pylops/__init__.py

Lines changed: 61 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,70 @@
1-
# isort: skip_file
2-
from .LinearOperator import LinearOperator
3-
from .basicoperators import Regression
4-
from .basicoperators import LinearRegression
5-
from .basicoperators import MatrixMult
6-
from .basicoperators import Identity
7-
from .basicoperators import Zero
8-
from .basicoperators import Diagonal
9-
from .basicoperators import Flip
10-
from .basicoperators import Symmetrize
11-
from .basicoperators import Spread
12-
from .basicoperators import Transpose
13-
from .basicoperators import Roll
14-
from .basicoperators import Pad
15-
from .basicoperators import Sum
16-
from .basicoperators import FunctionOperator
17-
from .basicoperators import MemoizeOperator
1+
"""
2+
PyLops
3+
======
184
19-
from .basicoperators import VStack
20-
from .basicoperators import HStack
21-
from .basicoperators import Block
22-
from .basicoperators import BlockDiag
23-
from .basicoperators import Kronecker
24-
from .basicoperators import Real
25-
from .basicoperators import Imag
26-
from .basicoperators import Conj
5+
Linear operators and inverse problems are at the core of many of the most used
6+
algorithms in signal processing, image processing, and remote sensing.
7+
When dealing with small-scale problems, the Python numerical scientific
8+
libraries `numpy <http://www.numpy.org>`_
9+
and `scipy <http://www.scipy.org/scipylib/index.html>`_ allow to perform most
10+
of the underlying matrix operations (e.g., computation of matrix-vector
11+
products and manipulation of matrices) in a simple and expressive way.
2712
28-
from .basicoperators import CausalIntegration
29-
from .basicoperators import FirstDerivative
30-
from .basicoperators import SecondDerivative
31-
from .basicoperators import Laplacian
32-
from .basicoperators import Gradient
33-
from .basicoperators import FirstDirectionalDerivative
34-
from .basicoperators import SecondDirectionalDerivative
35-
from .basicoperators import Restriction
36-
from .basicoperators import Smoothing1D
37-
from .basicoperators import Smoothing2D
13+
Many useful operators, however, do not lend themselves to an explicit matrix
14+
representation when used to solve large-scale problems. PyLops operators,
15+
on the other hand, still represent a matrix and can be treated in a similar
16+
way, but do not rely on the explicit creation of a dense (or sparse) matrix
17+
itself. Conversely, the forward and adjoint operators are represented by small
18+
pieces of codes that mimic the effect of the matrix on a vector or
19+
another matrix.
3820
39-
from .avo.poststack import PoststackLinearModelling
40-
from .avo.prestack import PrestackWaveletModelling, PrestackLinearModelling
21+
Luckily, many iterative methods (e.g. cg, lsqr) do not need to know the
22+
individual entries of a matrix to solve a linear system. Such solvers only
23+
require the computation of forward and adjoint matrix-vector products as
24+
done for any of the PyLops operators.
4125
42-
from .optimization.solver import cg, cgls
43-
from .optimization.leastsquares import NormalEquationsInversion, RegularizedInversion
44-
from .optimization.leastsquares import PreconditionedInversion
45-
from .optimization.sparsity import IRLS, OMP, ISTA, FISTA, SPGL1, SplitBregman
26+
PyLops provides
27+
1. A general construct for creating Linear Operators
28+
2. An extensive set of commonly used linear operators
29+
3. A set of least-squares and sparse solvers for linear operators.
4630
47-
from .utils.seismicevents import makeaxis, linear2d, parabolic2d
48-
from .utils.tapers import hanningtaper, cosinetaper, taper2d, taper3d
49-
from .utils.wavelets import ricker, gaussian
50-
from .utils.utils import Report
51-
from .utils.deps import *
31+
Available subpackages
32+
---------------------
33+
basicoperators
34+
Basic Linear Operators
35+
signalprocessing
36+
Linear Operators for Signal Processing operations
37+
avo
38+
Linear Operators for Seismic Reservoir Characterization
39+
waveeqprocessing
40+
Linear Operators for Wave Equation oriented processing
41+
optimization
42+
Solvers
43+
utils
44+
Utility routines
45+
46+
"""
5247

53-
from . import avo
54-
from . import basicoperators
55-
from . import optimization
56-
from . import signalprocessing
57-
from . import utils
58-
from . import waveeqprocessing
48+
from .LinearOperator import LinearOperator
49+
from .basicoperators import *
50+
51+
from . import (
52+
avo,
53+
basicoperators,
54+
optimization,
55+
signalprocessing,
56+
utils,
57+
waveeqprocessing,
58+
)
59+
from .avo.poststack import *
60+
from .avo.prestack import *
61+
from .optimization.solver import *
62+
from .optimization.leastsquares import *
63+
from .optimization.sparsity import *
64+
from .utils.seismicevents import *
65+
from .utils.tapers import *
66+
from .utils.utils import Report
67+
from .utils.wavelets import *
5968

6069
try:
6170
from .version import version as __version__

pylops/avo/__init__.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,34 @@
1-
# isort: skip_file
1+
"""
2+
AVO Operators
3+
=============
4+
5+
The subpackage avo provides linear operators and applications aimed at
6+
solving various inverse problems in the area of Seismic Reservoir
7+
Characterization.
8+
9+
A list of available operators present in pylops.avo:
10+
11+
AVOLinearModelling AVO modelling.
12+
PoststackLinearModelling Post-stack seismic modelling.
13+
PrestackLinearModelling Pre-stack seismic modelling.
14+
PrestackWaveletModelling Pre-stack modelling operator for wavelet.
15+
16+
and a list of applications:
17+
18+
PoststackInversion Post-stack seismic inversion.
19+
PrestackInversion Pre-stack seismic inversion.
20+
21+
"""
22+
23+
from .poststack import *
24+
from .prestack import *
25+
26+
27+
__all__ = [
28+
"AVOLinearModelling",
29+
"PoststackLinearModelling",
30+
"PrestackWaveletModelling",
31+
"PrestackLinearModelling",
32+
"PoststackInversion",
33+
"PrestackInversion",
34+
]

0 commit comments

Comments
 (0)