Skip to content

Commit 7204a71

Browse files
authored
Merge pull request #772 from mrava87/feat-slopeest_cupy
Feat: slope_estimate cupy backend
2 parents 4ec70b3 + fdcedae commit 7204a71

3 files changed

Lines changed: 40 additions & 15 deletions

File tree

‎pylops/utils/backend.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"get_sliding_window_view",
1212
"get_block_diag",
1313
"get_toeplitz",
14+
"get_gaussian_filter",
1415
"get_csc_matrix",
1516
"get_csr_matrix",
1617
"get_dia_matrix",
@@ -36,6 +37,7 @@
3637
import numpy as np
3738
import scipy.fft as sp_fft
3839
from scipy.linalg import block_diag, lstsq, toeplitz
40+
from scipy.ndimage import gaussian_filter
3941
from scipy.signal import convolve, correlate, fftconvolve, oaconvolve
4042
from scipy.sparse import csc_matrix, csr_matrix, dia_matrix, eye
4143

@@ -48,6 +50,7 @@
4850
import cupyx.scipy.fft as cp_fft
4951
from cupyx.scipy.linalg import block_diag as cp_block_diag
5052
from cupyx.scipy.linalg import toeplitz as cp_toeplitz
53+
from cupyx.scipy.ndimage import gaussian_filter as cp_gaussian_filter
5154
from cupyx.scipy.signal import convolve as cp_convolve
5255
from cupyx.scipy.signal import correlate as cp_correlate
5356
from cupyx.scipy.signal import fftconvolve as cp_fftconvolve
@@ -363,6 +366,29 @@ def get_toeplitz(x: ArrayLike) -> Callable:
363366
return toeplitz
364367

365368

369+
def get_gaussian_filter(x: ArrayLike) -> Callable:
370+
"""Returns correct gaussian_filter module based on input
371+
372+
Parameters
373+
----------
374+
x : :obj:`numpy.ndarray`
375+
Array
376+
377+
Returns
378+
-------
379+
f : :obj:`callable`
380+
Function to be used to process array
381+
382+
"""
383+
if not deps.cupy_enabled:
384+
return gaussian_filter
385+
386+
if cp.get_array_module(x) == np:
387+
return gaussian_filter
388+
else:
389+
return cp_gaussian_filter
390+
391+
366392
def get_csc_matrix(x: ArrayLike) -> Callable:
367393
"""Returns correct csc_matrix module based on input
368394

‎pylops/utils/signalprocessing.py‎

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from collections.abc import Sequence
1111

1212
import numpy as np
13-
from scipy.ndimage import gaussian_filter
1413

1514
from pylops.basicoperators import Diagonal, Smoothing2D, SmoothingND
1615
from pylops.optimization.leastsquares import preconditioned_inversion
@@ -20,6 +19,7 @@
2019
get_array_module,
2120
get_csr_matrix,
2221
get_dia_matrix,
22+
get_gaussian_filter,
2323
get_normalize_axis_index,
2424
get_toeplitz,
2525
)
@@ -266,37 +266,39 @@ def slope_estimate(
266266
anisotropy in digitized images", Journal ASCI Imaging Workshop. 1995.
267267
268268
"""
269-
slopes = np.zeros_like(d)
270-
anisos = np.zeros_like(d)
269+
ncp = get_array_module(d)
271270

272-
gz, gx = np.gradient(d, dz, dx)
271+
slopes = ncp.zeros_like(d)
272+
anisos = ncp.zeros_like(d)
273+
274+
gz, gx = ncp.gradient(d, dz, dx)
273275
gzz, gzx, gxx = gz * gz, gz * gx, gx * gx
274276

275277
# smoothing
276-
gzz = gaussian_filter(gzz, sigma=smooth)
277-
gzx = gaussian_filter(gzx, sigma=smooth)
278-
gxx = gaussian_filter(gxx, sigma=smooth)
278+
gzz = get_gaussian_filter(d)(gzz, sigma=smooth)
279+
gzx = get_gaussian_filter(d)(gzx, sigma=smooth)
280+
gxx = get_gaussian_filter(d)(gxx, sigma=smooth)
279281

280-
gmax = max(gzz.max(), gxx.max(), np.abs(gzx).max())
282+
gmax = max(gzz.max(), gxx.max(), ncp.abs(gzx).max())
281283
if gmax <= eps:
282-
return np.zeros_like(d), anisos
284+
return ncp.zeros_like(d), anisos
283285

284286
gzz /= gmax
285287
gzx /= gmax
286288
gxx /= gmax
287289

288290
lcommon1 = 0.5 * (gzz + gxx)
289-
lcommon2 = 0.5 * np.sqrt((gzz - gxx) ** 2 + 4 * gzx**2)
291+
lcommon2 = 0.5 * ncp.sqrt((gzz - gxx) ** 2 + 4 * gzx**2)
290292
l1 = lcommon1 + lcommon2
291293
l2 = lcommon1 - lcommon2
292294

293295
regdata = l1 > eps
294296
anisos[regdata] = 1 - l2[regdata] / l1[regdata]
295297

296298
if dips:
297-
slopes = 0.5 * np.arctan2(2 * gzx, gzz - gxx)
299+
slopes = 0.5 * ncp.arctan2(2 * gzx, gzz - gxx)
298300
else:
299-
regdata = np.abs(gzx) > eps
301+
regdata = ncp.abs(gzx) > eps
300302
slopes[regdata] = (l1 - gzz)[regdata] / gzx[regdata]
301303

302304
return slopes, anisos

‎pytests/test_signalutils.py‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,6 @@ def test_nonstationary_convmtx(par, sparse):
112112
assert_array_almost_equal(y, y1, decimal=4)
113113

114114

115-
@pytest.mark.skipif(
116-
int(os.environ.get("TEST_CUPY_PYLOPS", 0)) == 1, reason="Not CuPy enabled"
117-
)
118115
def test_slope_estimation_dips():
119116
"""Slope estimation using the Structure tensor algorithm should
120117
apply regularisation (some slopes are set to zero)

0 commit comments

Comments
 (0)