Skip to content

Commit 1d543a9

Browse files
authored
Merge pull request #215 from DiamondLightSource/remove_paganin_savu
retiring paganin savu method
2 parents ecc7ec9 + 3ba8579 commit 1d543a9

File tree

5 files changed

+58
-364
lines changed

5 files changed

+58
-364
lines changed

httomolibgpu/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from httomolibgpu.misc.rescale import rescale_to_int
55
from httomolibgpu.prep.alignment import distortion_correction_proj_discorpy
66
from httomolibgpu.prep.normalize import normalize
7-
from httomolibgpu.prep.phase import paganin_filter_savu, paganin_filter_tomopy
7+
from httomolibgpu.prep.phase import paganin_filter_tomopy
88
from httomolibgpu.prep.stripe import (
99
remove_stripe_based_sorting,
1010
remove_stripe_ti,

httomolibgpu/cuda_kernels/paganin_filter_gen.cu

Lines changed: 0 additions & 37 deletions
This file was deleted.

httomolibgpu/prep/phase.py

Lines changed: 54 additions & 217 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,8 @@
2929
from unittest.mock import Mock
3030

3131
if cupy_run:
32-
from httomolibgpu.cuda_kernels import load_cuda_module
3332
from cupyx.scipy.fft import fft2, ifft2, fftshift
3433
else:
35-
load_cuda_module = Mock()
3634
fft2 = Mock()
3735
ifft2 = Mock()
3836
fftshift = Mock()
@@ -44,225 +42,10 @@
4442
from httomolibgpu.misc.supp_func import data_checker
4543

4644
__all__ = [
47-
"paganin_filter_savu",
4845
"paganin_filter_tomopy",
4946
]
5047

5148

52-
## %%%%%%%%%%%%%%%%%%%%%%% paganin_filter %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ##
53-
#: CuPy implementation of Paganin filter from Savu
54-
def paganin_filter_savu(
55-
data: cp.ndarray,
56-
ratio: float = 250.0,
57-
energy: float = 53.0,
58-
distance: float = 1.0,
59-
resolution: float = 1.28,
60-
pad_y: int = 100,
61-
pad_x: int = 100,
62-
pad_method: str = "edge",
63-
increment: float = 0.0,
64-
) -> cp.ndarray:
65-
"""
66-
Apply Paganin filter (for denoising or contrast enhancement) to
67-
projections.
68-
69-
Parameters
70-
----------
71-
data : cp.ndarray
72-
The stack of projections to filter.
73-
74-
ratio : float, optional
75-
Ratio of delta/beta.
76-
77-
energy : float, optional
78-
Beam energy in keV.
79-
80-
distance : float, optional
81-
Distance from sample to detector in metres.
82-
83-
resolution : float, optional
84-
Pixel size in microns.
85-
86-
pad_y : int, optional
87-
Pad the top and bottom of projections.
88-
89-
pad_x : int, optional
90-
Pad the left and right of projections.
91-
92-
pad_method : str, optional
93-
Numpy pad method to use.
94-
95-
increment : float, optional
96-
Increment all values by this amount before taking the log.
97-
98-
Returns
99-
-------
100-
cp.ndarray
101-
The stack of filtered projections.
102-
"""
103-
# Check the input data is valid
104-
if data.ndim != 3:
105-
raise ValueError(
106-
f"Invalid number of dimensions in data: {data.ndim},"
107-
" please provide a stack of 2D projections."
108-
)
109-
110-
data = data_checker(data, verbosity=True, method_name="paganin_filter_savu")
111-
112-
# Setup various values for the filter
113-
_, height, width = data.shape
114-
micron = 1e-6
115-
keV = 1000.0
116-
energy *= keV
117-
resolution *= micron
118-
wavelength = (1240.0 / energy) * 1e-9
119-
120-
height1 = height + 2 * pad_y
121-
width1 = width + 2 * pad_x
122-
123-
# Define the paganin filter, taking into account the padding that will be
124-
# applied to the projections (if any)
125-
126-
# Using raw kernel her as indexing is direct and it avoids a lot of temporaries
127-
# and tiny kernels
128-
module = load_cuda_module("paganin_filter_gen")
129-
kernel = module.get_function("paganin_filter_gen")
130-
131-
# Apply padding to all the 2D projections
132-
# Note: this takes considerable time on GPU...
133-
data = cp.pad(data, ((0, 0), (pad_y, pad_y), (pad_x, pad_x)), mode=pad_method)
134-
135-
precond_kernel_float = cp.ElementwiseKernel(
136-
"T data",
137-
"T out",
138-
"""
139-
if (isnan(data)) {
140-
out = T(0);
141-
} else if (isinf(data)) {
142-
out = data < 0.0 ? -3.402823e38f : 3.402823e38f; // FLT_MAX, not available in cupy
143-
} else if (data == 0.0) {
144-
out = 1.0;
145-
} else {
146-
out = data;
147-
}
148-
""",
149-
name="paganin_precond_float",
150-
no_return=True,
151-
)
152-
precond_kernel_int = cp.ElementwiseKernel(
153-
"T data",
154-
"T out",
155-
"""out = data == 0 ? 1 : data""",
156-
name="paganin_precond_int",
157-
no_return=True,
158-
)
159-
160-
if data.dtype in (cp.float32, cp.float64):
161-
precond_kernel_float(data, data)
162-
else:
163-
precond_kernel_int(data, data)
164-
165-
# avoid normalising in both directions - we include multiplier in the post_kernel
166-
data = cp.asarray(data, dtype=cp.complex64)
167-
data = fft2(data, axes=(-2, -1), overwrite_x=True, norm="backward")
168-
169-
# prepare filter here, while the GPU is busy with the FFT
170-
filtercomplex = cp.empty((height1, width1), dtype=cp.complex64)
171-
bx = 16
172-
by = 8
173-
gx = (width1 + bx - 1) // bx
174-
gy = (height1 + by - 1) // by
175-
kernel(
176-
grid=(gx, gy, 1),
177-
block=(bx, by, 1),
178-
args=(
179-
cp.int32(width1),
180-
cp.int32(height1),
181-
cp.float32(resolution),
182-
cp.float32(wavelength),
183-
cp.float32(distance),
184-
cp.float32(ratio),
185-
filtercomplex,
186-
),
187-
)
188-
data *= filtercomplex
189-
190-
data = ifft2(data, axes=(-2, -1), overwrite_x=True, norm="forward")
191-
192-
post_kernel = cp.ElementwiseKernel(
193-
"C pci1, raw float32 increment, raw float32 ratio, raw float32 fft_scale",
194-
"T out",
195-
"out = -0.5 * ratio * log(abs(pci1) * fft_scale + increment)",
196-
name="paganin_post_proc",
197-
no_return=True,
198-
)
199-
fft_scale = 1.0 / (data.shape[1] * data.shape[2])
200-
res = cp.empty((data.shape[0], height, width), dtype=cp.float32)
201-
post_kernel(
202-
data[:, pad_y : pad_y + height, pad_x : pad_x + width],
203-
np.float32(increment),
204-
np.float32(ratio),
205-
np.float32(fft_scale),
206-
res,
207-
)
208-
return res
209-
210-
211-
def _wavelength(energy: float) -> float:
212-
SPEED_OF_LIGHT = 299792458e2 # [cm/s]
213-
PLANCK_CONSTANT = 6.58211928e-19 # [keV*s]
214-
return 2 * math.pi * PLANCK_CONSTANT * SPEED_OF_LIGHT / energy
215-
216-
217-
def _reciprocal_grid(pixel_size: float, shape_proj: tuple) -> cp.ndarray:
218-
"""
219-
Calculate reciprocal grid.
220-
221-
Parameters
222-
----------
223-
pixel_size : float
224-
Detector pixel size in cm.
225-
shape_proj : tuple
226-
Shape of the reciprocal grid along x and y axes.
227-
228-
Returns
229-
-------
230-
ndarray
231-
Grid coordinates.
232-
"""
233-
# Sampling in reciprocal space.
234-
indx = _reciprocal_coord(pixel_size, shape_proj[0])
235-
indy = _reciprocal_coord(pixel_size, shape_proj[1])
236-
indx_sq = cp.square(indx)
237-
indy_sq = cp.square(indy)
238-
239-
return cp.add.outer(indx_sq, indy_sq)
240-
241-
242-
def _reciprocal_coord(pixel_size: float, num_grid: int) -> cp.ndarray:
243-
"""
244-
Calculate reciprocal grid coordinates for a given pixel size
245-
and discretization.
246-
247-
Parameters
248-
----------
249-
pixel_size : float
250-
Detector pixel size in cm.
251-
num_grid : int
252-
Size of the reciprocal grid.
253-
254-
Returns
255-
-------
256-
ndarray
257-
Grid coordinates.
258-
"""
259-
n = num_grid - 1
260-
rc = cp.arange(-n, num_grid, 2, dtype=cp.float32)
261-
rc *= 2 * math.pi / (n * pixel_size)
262-
return rc
263-
264-
265-
##-------------------------------------------------------------##
26649
##-------------------------------------------------------------##
26750
# Adaptation of retrieve_phase (Paganin filter) from TomoPy
26851
def paganin_filter_tomopy(
@@ -412,3 +195,57 @@ def _pad_projections_to_second_power(
412195
def _paganin_filter_factor2(energy, dist, alpha, w2):
413196
# Alpha represents the ratio of delta/beta.
414197
return 1 / (_wavelength(energy) * dist * w2 / (4 * math.pi) + alpha)
198+
199+
200+
def _wavelength(energy: float) -> float:
201+
SPEED_OF_LIGHT = 299792458e2 # [cm/s]
202+
PLANCK_CONSTANT = 6.58211928e-19 # [keV*s]
203+
return 2 * math.pi * PLANCK_CONSTANT * SPEED_OF_LIGHT / energy
204+
205+
206+
def _reciprocal_grid(pixel_size: float, shape_proj: tuple) -> cp.ndarray:
207+
"""
208+
Calculate reciprocal grid.
209+
210+
Parameters
211+
----------
212+
pixel_size : float
213+
Detector pixel size in cm.
214+
shape_proj : tuple
215+
Shape of the reciprocal grid along x and y axes.
216+
217+
Returns
218+
-------
219+
ndarray
220+
Grid coordinates.
221+
"""
222+
# Sampling in reciprocal space.
223+
indx = _reciprocal_coord(pixel_size, shape_proj[0])
224+
indy = _reciprocal_coord(pixel_size, shape_proj[1])
225+
indx_sq = cp.square(indx)
226+
indy_sq = cp.square(indy)
227+
228+
return cp.add.outer(indx_sq, indy_sq)
229+
230+
231+
def _reciprocal_coord(pixel_size: float, num_grid: int) -> cp.ndarray:
232+
"""
233+
Calculate reciprocal grid coordinates for a given pixel size
234+
and discretization.
235+
236+
Parameters
237+
----------
238+
pixel_size : float
239+
Detector pixel size in cm.
240+
num_grid : int
241+
Size of the reciprocal grid.
242+
243+
Returns
244+
-------
245+
ndarray
246+
Grid coordinates.
247+
"""
248+
n = num_grid - 1
249+
rc = cp.arange(-n, num_grid, 2, dtype=cp.float32)
250+
rc *= 2 * math.pi / (n * pixel_size)
251+
return rc

httomolibgpu/recon/algorithm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def FBP3d_tomobar(
152152
"""
153153
Perform Filtered Backprojection (FBP) reconstruction using ASTRA toolbox :cite:`van2016fast` and
154154
ToMoBAR :cite:`kazantsev2020tomographic` wrappers.
155-
This is a 3D recon from the CuPy array directly and using a custom built SINC filter for filtration in Fourier space,
155+
This is a 3D recon from the CuPy array directly and using a custom built SINC filter for filtration in Fourier space,
156156
see more in :ref:`method_FBP3d_tomobar`.
157157
158158
Parameters
@@ -166,7 +166,7 @@ def FBP3d_tomobar(
166166
detector_pad : int
167167
Detector width padding with edge values to remove circle/arc type artifacts in the reconstruction.
168168
filter_freq_cutoff : float
169-
Cutoff frequency parameter for the SINC filter, the lower values may produce better contrast but noisy reconstruction. The filter change will also affect the dynamic range of the reconstructed image.
169+
Cutoff frequency parameter for the SINC filter, the lower values may produce better contrast but noisy reconstruction. The filter change will also affect the dynamic range of the reconstructed image.
170170
recon_size : int, optional
171171
The [recon_size, recon_size] shape of the reconstructed slice in pixels.
172172
By default (None), the reconstructed size will be the dimension of the horizontal detector.

0 commit comments

Comments
 (0)