Skip to content

Commit af3d4c3

Browse files
authored
Merge pull request #238 from DiamondLightSource/paganin_changes
Paganin filter changes
2 parents facc027 + e9c9bf3 commit af3d4c3

File tree

13 files changed

+210
-82
lines changed

13 files changed

+210
-82
lines changed
212 KB
Loading
214 KB
Loading
156 KB
Loading
45.4 KB
Loading
82 KB
Loading

docs/source/bibliography/references.bib

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ @article{Paganin02
88
year={2002},
99
publisher={Wiley Online Library}
1010
}
11+
@article{paganin2020boosting,
12+
title={Boosting spatial resolution by incorporating periodic boundary conditions into single-distance hard-x-ray phase retrieval},
13+
author={Paganin, David M and Favre-Nicolin, Vincent and Mirone, Alessandro and Rack, Alexander and Villanova, Julie and Olbinado, Margie P and Fernandez, Vincent and da Silva, Julio C and Pelliccia, Daniele},
14+
journal={Journal of Optics},
15+
volume={22},
16+
number={11},
17+
pages={115607},
18+
year={2020},
19+
publisher={IOP Publishing}
20+
}
1121
@article{vo2021data,
1222
title={Data processing methods and data acquisition for samples larger than the field of view in parallel-beam tomography},
1323
author={Vo, Nghia T and Atwood, Robert C and Drakopoulos, Michael and Connolley, Thomas},

docs/source/reference/methods.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ Here we present a list of methods of the HTTomolibGPU library with more detailed
1010

1111
methods_list/correction_methods
1212
methods_list/stripe_removal_methods
13+
methods_list/phase_contrast_methods
1314
methods_list/reconstruction_methods
1415
methods_list/denoising_methods
1516
methods_list/rescale_methods
16-
17-
17+
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
.. _phase_contrast_module:
2+
3+
Phase-contrast enhancement
4+
**************************
5+
6+
Methods from the :mod:`httomolibgpu.prep.phase` module needed when the data requires phase contrast enhancement.
7+
8+
**Description**
9+
10+
In conventional X-ray tomography, image contrast comes mainly from absorption, i.e., how much a material attenuates X-rays.
11+
For some samples, especially biological, the absorption difference between structures can be insignificant and therefore the edge contrast
12+
can be lost. X-rays also undergo phase shifts when passing through materials with different refractive indices and
13+
these phase shifts carry valuable structural information that can be converted into contrast if properly reconstructed.
14+
15+
The phase contrast enhances edges and interfaces, revealing fine internal structure that pure absorption tomography would miss.
16+
When we record propagation-based phase contrast images (e.g., at a certain distance from the sample), the detected intensity includes
17+
a mixture of absorption and phase effects. To reconstruct a clean 3D volume, we must separate phase from intensity.
18+
This process is called the phase retrieval. The Paganin method :cite:`Paganin02` provides a simple, robust way to do this.
19+
20+
The Paganin formula is the following, see eq.10 in :cite:`paganin2020boosting`:
21+
22+
.. math:: T(x,y) = - \frac{1}{\mu}\ln\left (\mathcal{F}^{-1}\left
23+
(\frac{\mathcal{F}\left [ I(x, y, z = \Delta) / I_{0} \right ]}{1 +
24+
\frac{\delta\Delta}{\mu}\left ( k_x^2 + k_y^2 \right )} \right )\right ),
25+
26+
where:
27+
28+
* :math:`I(x, y, z = \Delta)` - measured X-ray beam intensity at the propagation distance :math:`\Delta`.
29+
30+
* :math:`I_{0}` - measured X-ray beam intensity at the zero-distance from the X-ray source (incident beam).
31+
32+
* :math:`\Delta > 0` - propagation distance of the wavefront from sample to detector.
33+
34+
* :math:`\mu` - linear attenuation coefficient of the single-material object defined as :math:`\mu = 2k\beta`, where :math:`k=\frac{2\pi}{\lambda}` is the wave-number corresponding to the vacuum wavelength :math:`\lambda`.
35+
36+
* :math:`\delta` - the phase decrement, related to the phase shift of X-rays. It is the real part of the complex refractive index: :math:`n = (1 - \delta) + i \beta`.
37+
38+
* :math:`\beta` - the absorption index, related to the attenuation. It the complex part of the material refractive index: :math:`n = (1 - \delta) + i \beta`.
39+
40+
One can re-write the formula above as:
41+
42+
.. math:: T(x,y) = - \frac{1}{\mu}\ln\left (\mathcal{F}^{-1}\left
43+
(\frac{\mathcal{F}\left [ I(x, y, z = \Delta) / I_{0} \right ]}{1 +
44+
\alpha \left ( k_x^2 + k_y^2 \right )} \right )\right ),
45+
46+
where:
47+
48+
.. math:: \alpha = \frac{\lambda \Delta \delta}{4 \pi \beta}.
49+
50+
**Where and how to use it:**
51+
52+
Works well for single-material and relatively homogeneous or weakly heterogeneous samples (e.g., biological tissues, polymers, soft matter). The filter is particularly useful for weakly absorbing samples imaged with hard X-rays, where traditional absorption contrast is poor.
53+
54+
**What are the adjustable parameters:**
55+
56+
* :code:`input data` is the flat/dark normalised raw data before the negative log. Note that the :math:`-ln()` operation is the part of the Paganin filter.
57+
58+
* :code:`pixel_size` Detector pixel size (resolution) in MICRON units.
59+
60+
* :code:`distance` Propagation distance (:math:`\Delta`) of the wavefront from sample to detector in METRE units.
61+
62+
* :code:`energy` Incident beam energy in keV.
63+
64+
* :code:`ratio_delta_beta` The ratio of :math:`\frac{\delta}{\beta}` is a critical parameter as it defines how the algorithm balances phase contrast versus absorption contrast. Higher ratio values lead to stronger smoothing, more phase contrast recovered, but potential loss of edge sharpness. It is recommended to keep :math:`\frac{\delta}{\beta} > 250` for weakly absorbing materials (like biological tissue, polymers, and light materials). Lower values :math:`\frac{\delta}{\beta} << 250` lead to less smoothing, more edges preserved.
65+
66+
**Real data examples:**
67+
68+
In this section we will be applying Paganin filter to data obtained at Diamond Light Source I12 beamline (beamtime NT41036-2, PI: G. Burca).
69+
The sample is a set of fixed bovine liver sections in a centrifuge tube. This is a biological sample and a good candidate for Paganin filter demonstration.
70+
71+
We used :ref:`method_remove_all_stripe` to remove ring artifacts and :ref:`method_LPRec3d_tomobar` for reconstruction of the filtered projections.
72+
We also used the following parameters for Paganin filter, while varying only :code:`ratio_delta_beta`.
73+
74+
.. code-block:: yaml
75+
76+
pixel_size: 32.4 # microns
77+
distance: 3.2 # meters
78+
energy: 53.0 # KeV
79+
80+
.. list-table::
81+
82+
83+
* - .. figure:: ../../_static/figures/paganin/lprec_no_paganin.jpg
84+
:width: 335px
85+
86+
No Paganin filter applied. The reconstruction is too noisy and without any features.
87+
88+
- .. figure:: ../../_static/figures/paganin/paganin_10.jpg
89+
:width: 335px
90+
91+
Paganin filter with :math:`\frac{\delta}{\beta} = 10`. Some features are recongnisable, but the image is still too noisy.
92+
93+
94+
.. list-table::
95+
96+
97+
* - .. figure:: ../../_static/figures/paganin/paganin_100.jpg
98+
:width: 200px
99+
100+
Paganin filter with :math:`\frac{\delta}{\beta} = 100`. Still a bit noisy.
101+
102+
- .. figure:: ../../_static/figures/paganin/paganin_500.jpg
103+
:width: 200px
104+
105+
Paganin filter with :math:`\frac{\delta}{\beta} = 500`. Close to the optimal value.
106+
107+
- .. figure:: ../../_static/figures/paganin/paganin_2000.jpg
108+
:width: 200px
109+
110+
Paganin filter with :math:`\frac{\delta}{\beta} = 2000`. Oversmoothed.

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_tomopy
7+
from httomolibgpu.prep.phase import paganin_filter
88
from httomolibgpu.prep.stripe import (
99
remove_stripe_based_sorting,
1010
remove_stripe_ti,

httomolibgpu/prep/phase.py

Lines changed: 40 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# Created By : Tomography Team at DLS <[email protected]>
1919
# Created Date: 01 November 2022
2020
# ---------------------------------------------------------------------------
21-
"""Modules for phase retrieval and phase-contrast enhancement"""
21+
"""Modules for phase retrieval and phase-contrast enhancement. For more detailed information, see :ref:`phase_contrast_module`."""
2222

2323
import numpy as np
2424
from httomolibgpu import cupywrapper
@@ -42,35 +42,36 @@
4242
from httomolibgpu.misc.supp_func import data_checker
4343

4444
__all__ = [
45-
"paganin_filter_tomopy",
45+
"paganin_filter",
4646
]
4747

4848

49-
##-------------------------------------------------------------##
50-
# Adaptation of retrieve_phase (Paganin filter) from TomoPy
51-
def paganin_filter_tomopy(
49+
# This implementation originated from the TomoPy version. It has been modified to conform
50+
# different unit standards and also control of the filter driven by 'delta/beta' ratio
51+
# as opposed to 'alpha' in the TomoPy's implementation.
52+
def paganin_filter(
5253
tomo: cp.ndarray,
53-
pixel_size: float = 1e-4,
54-
dist: float = 50.0,
54+
pixel_size: float = 1.28,
55+
distance: float = 1.0,
5556
energy: float = 53.0,
56-
alpha: float = 1e-3,
57+
ratio_delta_beta: float = 250,
5758
) -> cp.ndarray:
5859
"""
59-
Perform single-material phase retrieval from flats/darks corrected tomographic measurements. See
60-
:cite:`Paganin02` for a reference.
60+
Perform single-material phase retrieval from flats/darks corrected tomographic measurements. For more detailed information, see :ref:`phase_contrast_module`.
61+
Also see :cite:`Paganin02` and :cite:`paganin2020boosting` for references.
6162
6263
Parameters
6364
----------
6465
tomo : cp.ndarray
6566
3D array of f/d corrected tomographic projections.
66-
pixel_size : float, optional
67-
Detector pixel size in cm.
68-
dist : float, optional
69-
Propagation distance of the wavefront in cm.
70-
energy : float, optional
71-
Energy of incident wave in keV.
72-
alpha : float, optional
73-
Regularization parameter, the ratio of delta/beta. Smaller values lead to less noise and more blur.
67+
pixel_size : float
68+
Detector pixel size (resolution) in micron units.
69+
distance : float
70+
Propagation distance of the wavefront from sample to detector in metre units.
71+
energy : float
72+
Beam energy in keV.
73+
ratio_delta_beta : float
74+
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.
7475
7576
Returns
7677
-------
@@ -84,7 +85,7 @@ def paganin_filter_tomopy(
8485
" please provide a stack of 2D projections."
8586
)
8687

87-
tomo = data_checker(tomo, verbosity=True, method_name="paganin_filter_tomopy")
88+
tomo = data_checker(tomo, verbosity=True, method_name="paganin_filter")
8889

8990
dz_orig, dy_orig, dx_orig = tomo.shape
9091

@@ -98,11 +99,18 @@ def paganin_filter_tomopy(
9899
padded_tomo = cp.asarray(padded_tomo, dtype=cp.complex64)
99100
fft_tomo = fft2(padded_tomo, axes=(-2, -1), overwrite_x=True)
100101

101-
# Compute the reciprocal grid.
102-
w2 = _reciprocal_grid(pixel_size, (dy, dx))
102+
# calculate alpha constant
103+
alpha = _calculate_alpha(energy, distance / 1e-6, ratio_delta_beta)
104+
105+
# Compute the reciprocal grid
106+
indx = _reciprocal_coord(pixel_size, dy)
107+
indy = _reciprocal_coord(pixel_size, dx)
108+
109+
# Build Lorentzian-type filter
110+
phase_filter = fftshift(
111+
1.0 / (1.0 + alpha * (cp.add.outer(cp.square(indx), cp.square(indy))))
112+
)
103113

104-
# Build filter in the Fourier space.
105-
phase_filter = fftshift(_paganin_filter_factor2(energy, dist, alpha, w2))
106114
phase_filter = phase_filter / phase_filter.max() # normalisation
107115

108116
# Filter projections
@@ -132,6 +140,12 @@ def paganin_filter_tomopy(
132140
return _log_kernel(tomo)
133141

134142

143+
def _calculate_alpha(energy, distance_micron, ratio_delta_beta):
144+
return (
145+
_wavelength_micron(energy) * distance_micron / (4 * math.pi)
146+
) * ratio_delta_beta
147+
148+
135149
def _shift_bit_length(x: int) -> int:
136150
return 1 << (x - 1).bit_length()
137151

@@ -192,42 +206,12 @@ def _pad_projections_to_second_power(
192206
return padded_tomo, tuple(pad_list)
193207

194208

195-
def _paganin_filter_factor2(energy, dist, alpha, w2):
196-
# Alpha represents the ratio of delta/beta.
197-
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]
209+
def _wavelength_micron(energy: float) -> float:
210+
SPEED_OF_LIGHT = 299792458e2 * 10000.0 # [microns/s]
202211
PLANCK_CONSTANT = 6.58211928e-19 # [keV*s]
203212
return 2 * math.pi * PLANCK_CONSTANT * SPEED_OF_LIGHT / energy
204213

205214

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-
231215
def _reciprocal_coord(pixel_size: float, num_grid: int) -> cp.ndarray:
232216
"""
233217
Calculate reciprocal grid coordinates for a given pixel size
@@ -236,7 +220,7 @@ def _reciprocal_coord(pixel_size: float, num_grid: int) -> cp.ndarray:
236220
Parameters
237221
----------
238222
pixel_size : float
239-
Detector pixel size in cm.
223+
Detector pixel size in microns.
240224
num_grid : int
241225
Size of the reciprocal grid.
242226

0 commit comments

Comments
 (0)