httomolibgpu 5.0__tar.gz → 5.2__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.
Files changed (38) hide show
  1. {httomolibgpu-5.0/httomolibgpu.egg-info → httomolibgpu-5.2}/PKG-INFO +2 -1
  2. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/__init__.py +1 -0
  3. httomolibgpu-5.2/httomolibgpu/cuda_kernels/remove_stripe_fw.cu +155 -0
  4. httomolibgpu-5.2/httomolibgpu/memory_estimator_helpers.py +24 -0
  5. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/prep/phase.py +110 -22
  6. httomolibgpu-5.2/httomolibgpu/prep/stripe.py +1038 -0
  7. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/recon/_phase_cross_correlation.py +9 -25
  8. {httomolibgpu-5.0 → httomolibgpu-5.2/httomolibgpu.egg-info}/PKG-INFO +2 -1
  9. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu.egg-info/SOURCES.txt +2 -0
  10. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu.egg-info/requires.txt +1 -0
  11. {httomolibgpu-5.0 → httomolibgpu-5.2}/pyproject.toml +1 -0
  12. httomolibgpu-5.0/httomolibgpu/prep/stripe.py +0 -437
  13. {httomolibgpu-5.0 → httomolibgpu-5.2}/LICENSE +0 -0
  14. {httomolibgpu-5.0 → httomolibgpu-5.2}/MANIFEST.in +0 -0
  15. {httomolibgpu-5.0 → httomolibgpu-5.2}/README.rst +0 -0
  16. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/cuda_kernels/__init__.py +0 -0
  17. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/cuda_kernels/calc_metrics.cu +0 -0
  18. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/cuda_kernels/center_360_shifts.cu +0 -0
  19. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/cuda_kernels/generate_mask.cu +0 -0
  20. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/cuda_kernels/median_kernel.cu +0 -0
  21. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/cuda_kernels/raven_filter.cu +0 -0
  22. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/cuda_kernels/remove_nan_inf.cu +0 -0
  23. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/cupywrapper.py +0 -0
  24. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/misc/__init__.py +0 -0
  25. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/misc/corr.py +0 -0
  26. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/misc/denoise.py +0 -0
  27. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/misc/morph.py +0 -0
  28. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/misc/rescale.py +0 -0
  29. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/misc/utils.py +0 -0
  30. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/prep/__init__.py +0 -0
  31. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/prep/alignment.py +0 -0
  32. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/prep/normalize.py +0 -0
  33. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/recon/__init__.py +0 -0
  34. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/recon/algorithm.py +0 -0
  35. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu/recon/rotation.py +0 -0
  36. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu.egg-info/dependency_links.txt +0 -0
  37. {httomolibgpu-5.0 → httomolibgpu-5.2}/httomolibgpu.egg-info/top_level.txt +0 -0
  38. {httomolibgpu-5.0 → httomolibgpu-5.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: httomolibgpu
3
- Version: 5.0
3
+ Version: 5.2
4
4
  Summary: Commonly used tomography data processing methods at DLS.
5
5
  Author-email: Daniil Kazantsev <daniil.kazantsev@diamond.ac.uk>, Yousef Moazzam <yousef.moazzam@diamond.ac.uk>, Naman Gera <naman.gera@diamond.ac.uk>
6
6
  License: BSD-3-Clause
@@ -19,6 +19,7 @@ Requires-Dist: scipy
19
19
  Requires-Dist: pillow
20
20
  Requires-Dist: scikit-image
21
21
  Requires-Dist: tomobar
22
+ Requires-Dist: PyWavelets
22
23
  Provides-Extra: dev
23
24
  Requires-Dist: pytest; extra == "dev"
24
25
  Requires-Dist: pytest-cov; extra == "dev"
@@ -9,6 +9,7 @@ from httomolibgpu.prep.normalize import dark_flat_field_correction, minus_log
9
9
  from httomolibgpu.prep.phase import paganin_filter, paganin_filter_savu_legacy
10
10
  from httomolibgpu.prep.stripe import (
11
11
  remove_stripe_based_sorting,
12
+ remove_stripe_fw,
12
13
  remove_stripe_ti,
13
14
  remove_all_stripe,
14
15
  raven_filter,
@@ -0,0 +1,155 @@
1
+ template<int WSize>
2
+ __global__ void grouped_convolution_x(
3
+ int dim_x,
4
+ int dim_y,
5
+ int dim_z,
6
+ const float* in,
7
+ int in_stride_x,
8
+ int in_stride_y,
9
+ int in_stride_z,
10
+ float* out,
11
+ int out_stride_z,
12
+ int out_stride_group,
13
+ const float* w
14
+ )
15
+ {
16
+ const int g_thd_x = blockDim.x * blockIdx.x + threadIdx.x;
17
+ const int g_thd_y = blockDim.y * blockIdx.y + threadIdx.y;
18
+ const int g_thd_z = blockDim.z * blockIdx.z + threadIdx.z;
19
+ if (g_thd_x >= dim_x || g_thd_y >= dim_y || g_thd_z >= dim_z)
20
+ {
21
+ return;
22
+ }
23
+
24
+ constexpr int out_groups = 2;
25
+ for (int i = 0; i < out_groups; ++i)
26
+ {
27
+ float acc = 0.F;
28
+ for (int j = 0; j < WSize; ++j)
29
+ {
30
+ const int w_idx = i * WSize + j;
31
+ const int in_idx = (g_thd_x * in_stride_x + j) + g_thd_y * in_stride_y + g_thd_z * in_stride_z;
32
+ acc += w[w_idx] * in[in_idx];
33
+ }
34
+ const int out_idx = g_thd_x + g_thd_y * dim_x + g_thd_z * out_stride_z + i * out_stride_group;
35
+ out[out_idx] = acc;
36
+ }
37
+ }
38
+
39
+ template<int WSize>
40
+ __global__ void grouped_convolution_y(
41
+ int dim_x,
42
+ int dim_y,
43
+ int dim_z,
44
+ const float* in,
45
+ int in_stride_x,
46
+ int in_stride_y,
47
+ int in_stride_z,
48
+ int in_stride_group,
49
+ float* out,
50
+ int out_stride_z,
51
+ int out_stride_group,
52
+ const float* w
53
+ )
54
+ {
55
+ const int g_thd_x = blockDim.x * blockIdx.x + threadIdx.x;
56
+ const int g_thd_y = blockDim.y * blockIdx.y + threadIdx.y;
57
+ const int g_thd_z = blockDim.z * blockIdx.z + threadIdx.z;
58
+ if (g_thd_x >= dim_x || g_thd_y >= dim_y || g_thd_z >= dim_z)
59
+ {
60
+ return;
61
+ }
62
+
63
+ constexpr int in_groups = 2;
64
+ constexpr int out_groups = 2;
65
+ constexpr int item_stride_y = 2;
66
+ for (int group = 0; group < in_groups; ++group)
67
+ {
68
+ for (int i = 0; i < out_groups; ++i)
69
+ {
70
+ float acc = 0.F;
71
+ for (int j = 0; j < WSize; ++j)
72
+ {
73
+ const int w_idx = (out_groups * group + i) * WSize + j;
74
+ const int in_idx = g_thd_x * in_stride_x + (item_stride_y * g_thd_y + j) * in_stride_y + group * in_stride_group + g_thd_z * in_stride_z;
75
+ acc += w[w_idx] * in[in_idx];
76
+ }
77
+ const int out_idx = g_thd_x + g_thd_y * dim_x + g_thd_z * out_stride_z + (out_groups * group + i) * out_stride_group;
78
+ out[out_idx] = acc;
79
+ }
80
+ }
81
+ }
82
+
83
+ template<int WSize>
84
+ __global__ void transposed_convolution_x(
85
+ int dim_x,
86
+ int dim_y,
87
+ int dim_z,
88
+ const float* in,
89
+ int in_dim_x,
90
+ int in_stride_y,
91
+ int in_stride_z,
92
+ const float* w,
93
+ float* out
94
+ )
95
+ {
96
+ const int g_thd_x = blockDim.x * blockIdx.x + threadIdx.x;
97
+ const int g_thd_y = blockDim.y * blockIdx.y + threadIdx.y;
98
+ const int g_thd_z = blockDim.z * blockIdx.z + threadIdx.z;
99
+ if (g_thd_x >= dim_x || g_thd_y >= dim_y || g_thd_z >= dim_z)
100
+ {
101
+ return;
102
+ }
103
+
104
+ constexpr int item_out_stride = 2;
105
+ float acc = 0.F;
106
+ for (int i = 0; i < WSize; ++i)
107
+ {
108
+ const int in_x = (g_thd_x - i) / item_out_stride;
109
+ const int in_x_mod = (g_thd_x - i) % item_out_stride;
110
+ if (in_x_mod == 0 && in_x >= 0 && in_x < in_dim_x)
111
+ {
112
+ const int in_idx = in_x + g_thd_y * in_stride_y + g_thd_z * in_stride_z;
113
+ acc += in[in_idx] * w[i];
114
+ }
115
+ }
116
+ const int out_idx = g_thd_x + dim_x * g_thd_y + dim_x * dim_y * g_thd_z;
117
+ out[out_idx] = acc;
118
+ }
119
+
120
+ template<int WSize>
121
+ __global__ void transposed_convolution_y(
122
+ int dim_x,
123
+ int dim_y,
124
+ int dim_z,
125
+ const float* in,
126
+ int in_dim_y,
127
+ int in_stride_y,
128
+ int in_stride_z,
129
+ const float* w,
130
+ float* out
131
+ )
132
+ {
133
+ const int g_thd_x = blockDim.x * blockIdx.x + threadIdx.x;
134
+ const int g_thd_y = blockDim.y * blockIdx.y + threadIdx.y;
135
+ const int g_thd_z = blockDim.z * blockIdx.z + threadIdx.z;
136
+ if (g_thd_x >= dim_x || g_thd_y >= dim_y || g_thd_z >= dim_z)
137
+ {
138
+ return;
139
+ }
140
+
141
+ constexpr int item_out_stride = 2;
142
+ float acc = 0.F;
143
+ for (int i = 0; i < WSize; ++i)
144
+ {
145
+ const int in_y = (g_thd_y - i) / item_out_stride;
146
+ const int in_y_mod = (g_thd_y - i) % item_out_stride;
147
+ if (in_y_mod == 0 && in_y >= 0 && in_y < in_dim_y)
148
+ {
149
+ const int in_idx = g_thd_x + in_y * in_stride_y + g_thd_z * in_stride_z;
150
+ acc += in[in_idx] * w[i];
151
+ }
152
+ }
153
+ const int out_idx = g_thd_x + dim_x * g_thd_y + dim_x * dim_y * g_thd_z;
154
+ out[out_idx] = acc;
155
+ }
@@ -0,0 +1,24 @@
1
+ ALLOCATION_UNIT_SIZE = 512
2
+
3
+
4
+ class _DeviceMemStack:
5
+ def __init__(self) -> None:
6
+ self.allocations = []
7
+ self.current = 0
8
+ self.highwater = 0
9
+
10
+ def malloc(self, bytes):
11
+ self.allocations.append(bytes)
12
+ allocated = self._round_up(bytes)
13
+ self.current += allocated
14
+ self.highwater = max(self.current, self.highwater)
15
+
16
+ def free(self, bytes):
17
+ assert bytes in self.allocations
18
+ self.allocations.remove(bytes)
19
+ self.current -= self._round_up(bytes)
20
+ assert self.current >= 0
21
+
22
+ def _round_up(self, size):
23
+ size = (size + ALLOCATION_UNIT_SIZE - 1) // ALLOCATION_UNIT_SIZE
24
+ return size * ALLOCATION_UNIT_SIZE
@@ -22,6 +22,7 @@
22
22
 
23
23
  import numpy as np
24
24
  from httomolibgpu import cupywrapper
25
+ from httomolibgpu.memory_estimator_helpers import _DeviceMemStack
25
26
 
26
27
  cp = cupywrapper.cp
27
28
  cupy_run = cupywrapper.cupy_run
@@ -30,13 +31,14 @@ from unittest.mock import Mock
30
31
 
31
32
  if cupy_run:
32
33
  from cupyx.scipy.fft import fft2, ifft2, fftshift
34
+ from cupyx.scipy.fftpack import get_fft_plan
33
35
  else:
34
36
  fft2 = Mock()
35
37
  ifft2 = Mock()
36
38
  fftshift = Mock()
37
39
 
38
40
  from numpy import float32
39
- from typing import Tuple
41
+ from typing import Optional, Tuple
40
42
  import math
41
43
 
42
44
  __all__ = [
@@ -54,6 +56,7 @@ def paganin_filter(
54
56
  distance: float = 1.0,
55
57
  energy: float = 53.0,
56
58
  ratio_delta_beta: float = 250,
59
+ calc_peak_gpu_mem: bool = False,
57
60
  ) -> cp.ndarray:
58
61
  """
59
62
  Perform single-material phase retrieval from flats/darks corrected tomographic measurements. For more detailed information, see :ref:`phase_contrast_module`.
@@ -71,30 +74,50 @@ def paganin_filter(
71
74
  Beam energy in keV.
72
75
  ratio_delta_beta : float
73
76
  The ratio of delta/beta, where delta is the phase shift and real part of the complex material refractive index and beta is the absorption.
77
+ calc_peak_gpu_mem: bool
78
+ Parameter to support memory estimation in HTTomo. Irrelevant to the method itself and can be ignored by user.
74
79
 
75
80
  Returns
76
81
  -------
77
82
  cp.ndarray
78
83
  The 3D array of Paganin phase-filtered projection images.
79
84
  """
85
+ mem_stack = _DeviceMemStack() if calc_peak_gpu_mem else None
80
86
  # Check the input data is valid
81
- if tomo.ndim != 3:
87
+ if not mem_stack and tomo.ndim != 3:
82
88
  raise ValueError(
83
89
  f"Invalid number of dimensions in data: {tomo.ndim},"
84
90
  " please provide a stack of 2D projections."
85
91
  )
86
-
87
- dz_orig, dy_orig, dx_orig = tomo.shape
92
+ if mem_stack:
93
+ mem_stack.malloc(np.prod(tomo) * np.float32().itemsize)
94
+ dz_orig, dy_orig, dx_orig = tomo.shape if not mem_stack else tomo
88
95
 
89
96
  # Perform padding to the power of 2 as FFT is O(n*log(n)) complexity
90
97
  # TODO: adding other options of padding?
91
- padded_tomo, pad_tup = _pad_projections_to_second_power(tomo)
98
+ padded_tomo, pad_tup = _pad_projections_to_second_power(tomo, mem_stack)
92
99
 
93
- dz, dy, dx = padded_tomo.shape
100
+ dz, dy, dx = padded_tomo.shape if not mem_stack else padded_tomo
94
101
 
95
102
  # 3D FFT of tomo data
96
- padded_tomo = cp.asarray(padded_tomo, dtype=cp.complex64)
97
- fft_tomo = fft2(padded_tomo, axes=(-2, -1), overwrite_x=True)
103
+ if mem_stack:
104
+ mem_stack.malloc(np.prod(padded_tomo) * np.complex64().itemsize)
105
+ mem_stack.free(np.prod(padded_tomo) * np.float32().itemsize)
106
+ fft_input = cp.empty(padded_tomo, dtype=cp.complex64)
107
+ else:
108
+ padded_tomo = cp.asarray(padded_tomo, dtype=cp.complex64)
109
+ fft_input = padded_tomo
110
+
111
+ fft_plan = get_fft_plan(fft_input, axes=(-2, -1))
112
+ if mem_stack:
113
+ mem_stack.malloc(fft_plan.work_area.mem.size)
114
+ mem_stack.free(fft_plan.work_area.mem.size)
115
+ else:
116
+ with fft_plan:
117
+ fft_tomo = fft2(padded_tomo, axes=(-2, -1), overwrite_x=True)
118
+ del padded_tomo
119
+ del fft_input
120
+ del fft_plan
98
121
 
99
122
  # calculate alpha constant
100
123
  alpha = _calculate_alpha(energy, distance / 1e-6, ratio_delta_beta)
@@ -103,18 +126,56 @@ def paganin_filter(
103
126
  indx = _reciprocal_coord(pixel_size, dy)
104
127
  indy = _reciprocal_coord(pixel_size, dx)
105
128
 
106
- # Build Lorentzian-type filter
107
- phase_filter = fftshift(
108
- 1.0 / (1.0 + alpha * (cp.add.outer(cp.square(indx), cp.square(indy))))
109
- )
129
+ if mem_stack:
130
+ mem_stack.malloc(indx.size * indx.dtype.itemsize) # cp.asarray(indx)
131
+ mem_stack.malloc(indx.size * indx.dtype.itemsize) # cp.square
132
+ mem_stack.free(indx.size * indx.dtype.itemsize) # cp.asarray(indx)
133
+ mem_stack.malloc(indy.size * indy.dtype.itemsize) # cp.asarray(indy)
134
+ mem_stack.malloc(indy.size * indy.dtype.itemsize) # cp.square
135
+ mem_stack.free(indy.size * indy.dtype.itemsize) # cp.asarray(indy)
136
+
137
+ mem_stack.malloc(indx.size * indy.size * indx.dtype.itemsize) # cp.add.outer
138
+ mem_stack.free(indx.size * indx.dtype.itemsize) # cp.square
139
+ mem_stack.free(indy.size * indy.dtype.itemsize) # cp.square
140
+ mem_stack.malloc(indx.size * indy.size * indx.dtype.itemsize) # phase_filter
141
+ mem_stack.free(indx.size * indy.size * indx.dtype.itemsize) # cp.add.outer
142
+ mem_stack.free(indx.size * indy.size * indx.dtype.itemsize) # phase_filter
143
+
144
+ else:
145
+ # Build Lorentzian-type filter
146
+ phase_filter = fftshift(
147
+ 1.0
148
+ / (
149
+ 1.0
150
+ + alpha
151
+ * (
152
+ cp.add.outer(
153
+ cp.square(cp.asarray(indx)), cp.square(cp.asarray(indy))
154
+ )
155
+ )
156
+ )
157
+ )
110
158
 
111
- phase_filter = phase_filter / phase_filter.max() # normalisation
159
+ phase_filter = phase_filter / phase_filter.max() # normalisation
112
160
 
113
- # Filter projections
114
- fft_tomo *= phase_filter
161
+ # Filter projections
162
+ fft_tomo *= phase_filter
163
+ del phase_filter
115
164
 
116
165
  # Apply filter and take inverse FFT
117
- ifft_filtered_tomo = ifft2(fft_tomo, axes=(-2, -1), overwrite_x=True).real
166
+ ifft_input = (
167
+ fft_tomo if not mem_stack else cp.empty(padded_tomo, dtype=cp.complex64)
168
+ )
169
+ ifft_plan = get_fft_plan(ifft_input, axes=(-2, -1))
170
+ if mem_stack:
171
+ mem_stack.malloc(ifft_plan.work_area.mem.size)
172
+ mem_stack.free(ifft_plan.work_area.mem.size)
173
+ else:
174
+ with ifft_plan:
175
+ ifft_filtered_tomo = ifft2(fft_tomo, axes=(-2, -1), overwrite_x=True).real
176
+ del fft_tomo
177
+ del ifft_plan
178
+ del ifft_input
118
179
 
119
180
  # slicing indices for cropping
120
181
  slc_indices = (
@@ -123,8 +184,19 @@ def paganin_filter(
123
184
  slice(pad_tup[2][0], pad_tup[2][0] + dx_orig, 1),
124
185
  )
125
186
 
187
+ if mem_stack:
188
+ mem_stack.malloc(np.prod(tomo) * np.float32().itemsize) # astype(cp.float32)
189
+ mem_stack.free(
190
+ np.prod(padded_tomo) * np.complex64().itemsize
191
+ ) # ifft_filtered_tomo
192
+ mem_stack.malloc(
193
+ np.prod(tomo) * np.float32().itemsize
194
+ ) # return _log_kernel(tomo)
195
+ return mem_stack.highwater
196
+
126
197
  # crop the padded filtered data:
127
198
  tomo = ifft_filtered_tomo[slc_indices].astype(cp.float32)
199
+ del ifft_filtered_tomo
128
200
 
129
201
  # taking the negative log
130
202
  _log_kernel = cp.ElementwiseKernel(
@@ -177,7 +249,7 @@ def _calculate_pad_size(datashape: tuple) -> list:
177
249
 
178
250
 
179
251
  def _pad_projections_to_second_power(
180
- tomo: cp.ndarray,
252
+ tomo: cp.ndarray, mem_stack: Optional[_DeviceMemStack]
181
253
  ) -> Tuple[cp.ndarray, Tuple[int, int]]:
182
254
  """
183
255
  Performs padding of each projection to the next power of 2.
@@ -194,11 +266,17 @@ def _pad_projections_to_second_power(
194
266
  ndarray: padded 3d projection data
195
267
  tuple: a tuple with padding dimensions
196
268
  """
197
- full_shape_tomo = cp.shape(tomo)
269
+ full_shape_tomo = cp.shape(tomo) if not mem_stack else tomo
198
270
 
199
271
  pad_list = _calculate_pad_size(full_shape_tomo)
200
272
 
201
- padded_tomo = cp.pad(tomo, tuple(pad_list), "edge")
273
+ if mem_stack:
274
+ padded_tomo = [
275
+ sh + pad[0] + pad[1] for sh, pad in zip(full_shape_tomo, pad_list)
276
+ ]
277
+ mem_stack.malloc(np.prod(padded_tomo) * np.float32().itemsize)
278
+ else:
279
+ padded_tomo = cp.pad(tomo, tuple(pad_list), "edge")
202
280
 
203
281
  return padded_tomo, tuple(pad_list)
204
282
 
@@ -209,7 +287,7 @@ def _wavelength_micron(energy: float) -> float:
209
287
  return 2 * math.pi * PLANCK_CONSTANT * SPEED_OF_LIGHT / energy
210
288
 
211
289
 
212
- def _reciprocal_coord(pixel_size: float, num_grid: int) -> cp.ndarray:
290
+ def _reciprocal_coord(pixel_size: float, num_grid: int) -> np.ndarray:
213
291
  """
214
292
  Calculate reciprocal grid coordinates for a given pixel size
215
293
  and discretization.
@@ -227,7 +305,7 @@ def _reciprocal_coord(pixel_size: float, num_grid: int) -> cp.ndarray:
227
305
  Grid coordinates.
228
306
  """
229
307
  n = num_grid - 1
230
- rc = cp.arange(-n, num_grid, 2, dtype=cp.float32)
308
+ rc = np.arange(-n, num_grid, 2, dtype=cp.float32)
231
309
  rc *= 2 * math.pi / (n * pixel_size)
232
310
  return rc
233
311
 
@@ -238,6 +316,7 @@ def paganin_filter_savu_legacy(
238
316
  distance: float = 1.0,
239
317
  energy: float = 53.0,
240
318
  ratio_delta_beta: float = 250,
319
+ calc_peak_gpu_mem: bool = False,
241
320
  ) -> cp.ndarray:
242
321
  """
243
322
  Perform single-material phase retrieval from flats/darks corrected tomographic measurements. For more detailed information, see :ref:`phase_contrast_module`.
@@ -256,6 +335,8 @@ def paganin_filter_savu_legacy(
256
335
  Beam energy in keV.
257
336
  ratio_delta_beta : float
258
337
  The ratio of delta/beta, where delta is the phase shift and real part of the complex material refractive index and beta is the absorption.
338
+ calc_peak_gpu_mem: bool
339
+ Parameter to support memory estimation in HTTomo. Irrelevant to the method itself and can be ignored by user.
259
340
 
260
341
  Returns
261
342
  -------
@@ -263,4 +344,11 @@ def paganin_filter_savu_legacy(
263
344
  The 3D array of Paganin phase-filtered projection images.
264
345
  """
265
346
 
266
- return paganin_filter(tomo, pixel_size, distance, energy, ratio_delta_beta / 4)
347
+ return paganin_filter(
348
+ tomo,
349
+ pixel_size,
350
+ distance,
351
+ energy,
352
+ ratio_delta_beta / 4,
353
+ calc_peak_gpu_mem=calc_peak_gpu_mem,
354
+ )