Skip to content

Commit 7fcb559

Browse files
committed
savu paganin work6
1 parent 95c82ca commit 7fcb559

File tree

1 file changed

+11
-82
lines changed

1 file changed

+11
-82
lines changed

httomolibgpu/prep/phase.py

Lines changed: 11 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,15 @@ def paganin_filter(
9797
fft_tomo = fft2(padded_tomo, axes=(-2, -1), overwrite_x=True)
9898

9999
# calculate alpha constant
100-
alpha = _calculate_alpha(energy, distance, ratio_delta_beta)
100+
alpha = _calculate_alpha(energy, distance / 1e-6, ratio_delta_beta)
101101

102102
# Compute the reciprocal grid
103-
# pixel_size *= 1e6 # rescaling to meters
104103
indx = _reciprocal_coord(pixel_size, dy)
105104
indy = _reciprocal_coord(pixel_size, dx)
106105

107106
# Build Lorentzian-type filter
108107
phase_filter = fftshift(
109-
1.0
110-
/ (
111-
1.0
112-
+ alpha * (cp.add.outer(cp.square(indx), cp.square(indy)) / (4 * math.pi))
113-
)
108+
1.0 / (1.0 + alpha * (cp.add.outer(cp.square(indx), cp.square(indy))))
114109
)
115110

116111
phase_filter = phase_filter / phase_filter.max() # normalisation
@@ -142,18 +137,10 @@ def paganin_filter(
142137
return _log_kernel(tomo)
143138

144139

145-
def _calculate_alpha(energy, distance, ratio_delta_beta):
146-
return _wavelength(energy) * distance * ratio_delta_beta
147-
148-
149-
# the scaling is different here and doesn't follow the original formula
150-
def _calculate_alpha_savu(energy, distance_micron, ratio_delta_beta):
140+
def _calculate_alpha(energy, distance_micron, ratio_delta_beta):
151141
return (
152-
((1240.0 / energy) * 10.0 ** (-9))
153-
* distance_micron
154-
* ratio_delta_beta
155-
* math.pi
156-
)
142+
_wavelength_micron(energy) * distance_micron / (4 * math.pi)
143+
) * ratio_delta_beta
157144

158145

159146
def _shift_bit_length(x: int) -> int:
@@ -216,9 +203,10 @@ def _pad_projections_to_second_power(
216203
return padded_tomo, tuple(pad_list)
217204

218205

219-
def _wavelength(energy: float) -> float:
220-
# for photons: E = 1keV -> 1.23984193 nm
221-
return (1.23984193e-6) / energy
206+
def _wavelength_micron(energy: float) -> float:
207+
SPEED_OF_LIGHT = 299792458e2 * 10000.0 # [microns/s]
208+
PLANCK_CONSTANT = 6.58211928e-19 # [keV*s]
209+
return 2 * math.pi * PLANCK_CONSTANT * SPEED_OF_LIGHT / energy
222210

223211

224212
def _reciprocal_coord(pixel_size: float, num_grid: int) -> cp.ndarray:
@@ -254,7 +242,7 @@ def paganin_filter_savu_legacy(
254242
"""
255243
Perform single-material phase retrieval from flats/darks corrected tomographic measurements. For more detailed information, see :ref:`phase_contrast_module`.
256244
Also see :cite:`Paganin02` and :cite:`paganin2020boosting` for references. The ratio_delta_beta parameter here follows implementation in Savu software.
257-
The module will be retired in future in favour of paganin_filter. One can rescale parameter ratio_delta_beta / 4*2*pi to achieve the same effect in paganin_filter.
245+
The module will be retired in future in favour of paganin_filter. One can rescale parameter ratio_delta_beta / 4 to achieve the same effect in paganin_filter.
258246
259247
Parameters
260248
----------
@@ -274,64 +262,5 @@ def paganin_filter_savu_legacy(
274262
cp.ndarray
275263
The 3D array of Paganin phase-filtered projection images.
276264
"""
277-
# Check the input data is valid
278-
if tomo.ndim != 3:
279-
raise ValueError(
280-
f"Invalid number of dimensions in data: {tomo.ndim},"
281-
" please provide a stack of 2D projections."
282-
)
283-
284-
dz_orig, dy_orig, dx_orig = tomo.shape
285-
286-
# Perform padding to the power of 2 as FFT is O(n*log(n)) complexity
287-
# TODO: adding other options of padding?
288-
padded_tomo, pad_tup = _pad_projections_to_second_power(tomo)
289-
290-
dz, dy, dx = padded_tomo.shape
291-
292-
# 3D FFT of tomo data
293-
padded_tomo = cp.asarray(padded_tomo, dtype=cp.complex64)
294-
fft_tomo = fft2(padded_tomo, axes=(-2, -1), overwrite_x=True)
295-
296-
# calculate alpha constant as Savu does
297-
micron = 10 ** (-6)
298-
KeV = 1000.0
299-
alpha = _calculate_alpha_savu(energy * KeV, distance * micron, ratio_delta_beta)
300-
301-
# Compute the reciprocal grid
302-
# NOTE: pixel_size is ALREADY in microns, why it rescaled to micron again in Savu?
303-
indx = _reciprocal_coord(pixel_size * micron, dy)
304-
indy = _reciprocal_coord(pixel_size * micron, dx)
305-
306-
# Build Lorentzian-type filter
307-
phase_filter = fftshift(
308-
1.0 / (1.0 + alpha * (cp.add.outer(cp.square(indx), cp.square(indy))))
309-
)
310-
311-
phase_filter = phase_filter / phase_filter.max() # normalisation
312265

313-
# Filter projections
314-
fft_tomo *= phase_filter
315-
316-
# Apply filter and take inverse FFT
317-
ifft_filtered_tomo = ifft2(fft_tomo, axes=(-2, -1), overwrite_x=True).real
318-
319-
# slicing indices for cropping
320-
slc_indices = (
321-
slice(pad_tup[0][0], pad_tup[0][0] + dz_orig, 1),
322-
slice(pad_tup[1][0], pad_tup[1][0] + dy_orig, 1),
323-
slice(pad_tup[2][0], pad_tup[2][0] + dx_orig, 1),
324-
)
325-
326-
# crop the padded filtered data:
327-
tomo = ifft_filtered_tomo[slc_indices].astype(cp.float32)
328-
329-
# taking the negative log
330-
_log_kernel = cp.ElementwiseKernel(
331-
"C tomo",
332-
"C out",
333-
"out = -log(tomo)",
334-
name="log_kernel",
335-
)
336-
337-
return _log_kernel(tomo)
266+
return paganin_filter(tomo, pixel_size, distance, energy, ratio_delta_beta / 4)

0 commit comments

Comments
 (0)