Skip to content

Commit 460fd46

Browse files
committed
adding more zenodo tests and update default FBP parameters
1 parent 51aad78 commit 460fd46

File tree

9 files changed

+188
-43
lines changed

9 files changed

+188
-43
lines changed

httomolibgpu/recon/algorithm.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ def FBP(
5454
data: cp.ndarray,
5555
angles: np.ndarray,
5656
center: Optional[float] = None,
57-
filter_freq_cutoff: Optional[float] = 1.1,
57+
filter_freq_cutoff: Optional[float] = 0.35,
5858
recon_size: Optional[int] = None,
59-
recon_mask_radius: Optional[float] = None,
59+
recon_mask_radius: Optional[float] = 0.95,
6060
gpu_id: int = 0,
6161
) -> cp.ndarray:
6262
"""
6363
Perform Filtered Backprojection (FBP) reconstruction using ASTRA toolbox :cite:`van2016fast` and
6464
ToMoBAR :cite:`kazantsev2020tomographic` wrappers.
65-
This is a 3D recon from a CuPy array directly and a custom built filter.
65+
This is a 3D recon from the CuPy array directly and using a custom built SINC filter for filtration in Fourier space.
6666
6767
Parameters
6868
----------
@@ -72,16 +72,16 @@ def FBP(
7272
An array of angles given in radians.
7373
center : float, optional
7474
The center of rotation (CoR).
75-
filter_freq_cutoff : float, optional
76-
Cutoff frequency parameter for the sinc filter, the lowest values produce more crispy but noisy reconstruction.
75+
filter_freq_cutoff : float
76+
Cutoff frequency parameter for the SINC filter, the lower values produce better contrast but noisy reconstruction.
7777
recon_size : int, optional
7878
The [recon_size, recon_size] shape of the reconstructed slice in pixels.
7979
By default (None), the reconstructed size will be the dimension of the horizontal detector.
80-
recon_mask_radius: float, optional
80+
recon_mask_radius: float
8181
The radius of the circular mask that applies to the reconstructed slice in order to crop
82-
out some undesirable artifacts. The values outside the diameter will be set to zero.
83-
None by default, to see the effect of the mask try setting the value in the range [0.7-1.0].
84-
gpu_id : int, optional
82+
out some undesirable artifacts. The values outside the given diameter will be set to zero.
83+
It is recommended to keep the value in the range [0.7-1.0].
84+
gpu_id : int
8585
A GPU device index to perform operation on.
8686
8787
Returns
@@ -109,7 +109,7 @@ def LPRec(
109109
angles: np.ndarray,
110110
center: Optional[float] = None,
111111
recon_size: Optional[int] = None,
112-
recon_mask_radius: Optional[float] = None,
112+
recon_mask_radius: Optional[float] = 0.95,
113113
) -> cp.ndarray:
114114
"""
115115
Fourier direct inversion in 3D on unequally spaced (also called as Log-Polar) grids using
@@ -127,10 +127,10 @@ def LPRec(
127127
recon_size : int, optional
128128
The [recon_size, recon_size] shape of the reconstructed slice in pixels.
129129
By default (None), the reconstructed size will be the dimension of the horizontal detector.
130-
recon_mask_radius: float, optional
130+
recon_mask_radius: float
131131
The radius of the circular mask that applies to the reconstructed slice in order to crop
132-
out some undesirable artifacts. The values outside the diameter will be set to zero.
133-
None by default, to see the effect of the mask try setting the value in the range [0.7-1.0].
132+
out some undesirable artifacts. The values outside the given diameter will be set to zero.
133+
It is recommended to keep the value in the range [0.7-1.0].
134134
135135
Returns
136136
-------

tests/test_misc/test_morph.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,6 @@
66
from numpy.testing import assert_allclose
77
from httomolibgpu.misc.morph import sino_360_to_180, data_resampler
88

9-
10-
# @pytest.mark.parametrize("rotation", ["left", "right"])
11-
# def test_sino_360_to_180_realdata(ensure_clean_memory, sino3600, rotation):
12-
# shape_new = (3601, 3, 2560)
13-
# data3d = cp.zeros(shape_new, dtype=np.float32)
14-
# data3d[:, 0, :] = sino3600
15-
# data3d[:, 1, :] = sino3600
16-
# data3d[:, 2, :] = sino3600
17-
18-
# sino_360_to_180(data3d, overlap=900, rotation=rotation)
19-
20-
# assert data3d.shape == shape_new
21-
# assert data3d.dtype == np.float32
22-
# assert data3d.flags.c_contiguous
23-
24-
259
@pytest.mark.parametrize(
2610
"overlap, rotation",
2711
[

tests/test_recon/test_algorithm.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def test_reconstruct_FBP_1(data, flats, darks, ensure_clean_memory):
1919
np.linspace(0.0 * np.pi / 180.0, 180.0 * np.pi / 180.0, data.shape[0]),
2020
79.5,
2121
filter_freq_cutoff=1.1,
22+
recon_mask_radius=None,
2223
)
2324
assert recon_data.flags.c_contiguous
2425
recon_data = recon_data.get()
@@ -36,6 +37,7 @@ def test_reconstruct_FBP_2(data, flats, darks, ensure_clean_memory):
3637
np.linspace(5.0 * np.pi / 360.0, 180.0 * np.pi / 360.0, data.shape[0]),
3738
15.5,
3839
filter_freq_cutoff=1.1,
40+
recon_mask_radius=None,
3941
)
4042

4143
recon_data = recon_data.get()

tests/test_recon/test_rotation.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,6 @@ def test_find_center_vo_performance():
9393
assert "performance in ms" == duration_ms
9494

9595

96-
def test_find_center_360_ones():
97-
mat = cp.ones(shape=(100, 100, 100), dtype=cp.float32)
98-
99-
(cor, overlap, side, overlap_position) = find_center_360(mat)
100-
101-
assert_allclose(cor, 5.0)
102-
assert_allclose(overlap, 12.0)
103-
assert side == 0
104-
assert_allclose(overlap_position, 7.0)
105-
106-
10796
def test_find_center_360_data(data):
10897
eps = 1e-5
10998
(cor, overlap, side, overlap_pos) = find_center_360(data, norm=True, denoise=False)

zenodo-tests/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
import numpy as np
44
import pytest
55

6-
CUR_DIR = os.path.abspath(os.path.dirname(__file__))
7-
8-
96
def pytest_addoption(parser):
107
parser.addoption(
118
"--performance",
@@ -34,6 +31,9 @@ def pytest_collection_modifyitems(config, items):
3431
item.add_marker(skip_perf)
3532

3633

34+
#CUR_DIR = os.path.abspath(os.path.dirname(__file__))
35+
CUR_DIR = "/dls/science/users/kjy41806/zenodo-tests/"
36+
3737
@pytest.fixture(scope="session")
3838
def test_data_path():
3939
return os.path.join(CUR_DIR, "large_data_archive")

zenodo-tests/test_misc/__init__.py

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import time
2+
import cupy as cp
3+
import numpy as np
4+
from cupy.cuda import nvtx
5+
import pytest
6+
from numpy.testing import assert_allclose
7+
from httomolibgpu.misc.morph import sino_360_to_180
8+
from httomolibgpu.prep.normalize import normalize
9+
10+
11+
def test_sino_360_to_180_i13_dataset1(i13_dataset1, ensure_clean_memory):
12+
13+
projdata = i13_dataset1[0]
14+
flats = i13_dataset1[2]
15+
darks = i13_dataset1[3]
16+
del i13_dataset1
17+
18+
data_normalised = normalize(projdata, flats, darks, minus_log=True)
19+
del flats, darks, projdata
20+
ensure_clean_memory
21+
22+
stiched_data_180degrees = sino_360_to_180(data_normalised, overlap = 473.822265625, rotation = 'right')
23+
stiched_data_180degrees = stiched_data_180degrees.get()
24+
25+
assert_allclose(np.sum(stiched_data_180degrees), 28512826.0, rtol=1e-07, atol=1e-6)
26+
assert stiched_data_180degrees.shape == (3000, 10, 4646)
27+
assert stiched_data_180degrees.dtype == np.float32
28+
assert stiched_data_180degrees.flags.c_contiguous
29+
30+
31+
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import cupy as cp
2+
import numpy as np
3+
import pytest
4+
from cupy.cuda import nvtx
5+
import time
6+
7+
from httomolibgpu.prep.normalize import normalize
8+
from httomolibgpu.recon.algorithm import (
9+
FBP,
10+
LPRec,
11+
)
12+
from numpy.testing import assert_allclose
13+
import time
14+
import pytest
15+
from cupy.cuda import nvtx
16+
17+
def test_reconstruct_FBP_i12_dataset1(i12_dataset1, ensure_clean_memory):
18+
19+
projdata = i12_dataset1[0]
20+
angles = i12_dataset1[1]
21+
flats = i12_dataset1[2]
22+
darks = i12_dataset1[3]
23+
del i12_dataset1
24+
25+
data_normalised = normalize(projdata, flats, darks, minus_log=True)
26+
del flats, darks, projdata
27+
ensure_clean_memory
28+
29+
recon_data = FBP(
30+
data_normalised,
31+
np.deg2rad(angles),
32+
center=1253.75,
33+
filter_freq_cutoff=0.35,
34+
)
35+
assert recon_data.flags.c_contiguous
36+
recon_data = recon_data.get()
37+
assert_allclose(np.sum(recon_data), 46569.395, rtol=1e-07, atol=1e-6)
38+
assert recon_data.dtype == np.float32
39+
assert recon_data.shape == (2560, 50, 2560)
40+
41+
@pytest.mark.perf
42+
def test_FBP_performance_i13_dataset2(i13_dataset2, ensure_clean_memory):
43+
dev = cp.cuda.Device()
44+
projdata = i13_dataset2[0]
45+
angles = i13_dataset2[1]
46+
flats = i13_dataset2[2]
47+
darks = i13_dataset2[3]
48+
del i13_dataset2
49+
50+
data_normalised = normalize(projdata, flats, darks, minus_log=True)
51+
del flats, darks, projdata
52+
ensure_clean_memory
53+
54+
# cold run first
55+
FBP(
56+
data_normalised,
57+
np.deg2rad(angles),
58+
center=1253.75,
59+
filter_freq_cutoff=0.35,
60+
)
61+
dev.synchronize()
62+
63+
start = time.perf_counter_ns()
64+
nvtx.RangePush("Core")
65+
for _ in range(10):
66+
FBP(
67+
data_normalised,
68+
np.deg2rad(angles),
69+
center=1286.25,
70+
filter_freq_cutoff=0.35,
71+
)
72+
nvtx.RangePop()
73+
dev.synchronize()
74+
duration_ms = float(time.perf_counter_ns() - start) * 1e-6 / 10
75+
76+
assert "performance in ms" == duration_ms
77+
78+
79+
def test_reconstruct_LPREC_i13_dataset2(i13_dataset2, ensure_clean_memory):
80+
81+
projdata = i13_dataset2[0]
82+
angles = i13_dataset2[1]
83+
flats = i13_dataset2[2]
84+
darks = i13_dataset2[3]
85+
del i13_dataset2
86+
87+
data_normalised = normalize(projdata, flats, darks, minus_log=True)
88+
del flats, darks, projdata
89+
ensure_clean_memory
90+
91+
92+
recon_data = LPRec(
93+
data=data_normalised,
94+
angles=np.deg2rad(angles),
95+
center=1286.25,
96+
)
97+
assert recon_data.flags.c_contiguous
98+
recon_data = recon_data.get()
99+
100+
assert_allclose(np.sum(recon_data), 2044.9531, rtol=1e-07, atol=1e-6)
101+
assert recon_data.dtype == np.float32
102+
assert recon_data.shape == (2560, 10, 2560)
103+
104+
105+
@pytest.mark.perf
106+
def test_LPREC_performance_i13_dataset2(i13_dataset2, ensure_clean_memory):
107+
dev = cp.cuda.Device()
108+
projdata = i13_dataset2[0]
109+
angles = i13_dataset2[1]
110+
flats = i13_dataset2[2]
111+
darks = i13_dataset2[3]
112+
del i13_dataset2
113+
114+
data_normalised = normalize(projdata, flats, darks, minus_log=True)
115+
del flats, darks, projdata
116+
ensure_clean_memory
117+
118+
# cold run first
119+
LPRec(
120+
data=data_normalised,
121+
angles=np.deg2rad(angles),
122+
center=1286.25,
123+
)
124+
dev.synchronize()
125+
126+
start = time.perf_counter_ns()
127+
nvtx.RangePush("Core")
128+
for _ in range(10):
129+
LPRec(
130+
data=data_normalised,
131+
angles=np.deg2rad(angles),
132+
center=1286.25,
133+
)
134+
nvtx.RangePop()
135+
dev.synchronize()
136+
duration_ms = float(time.perf_counter_ns() - start) * 1e-6 / 10
137+
138+
assert "performance in ms" == duration_ms

zenodo-tests/test_recon/test_rotation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ def test_center_360_i13_dataset1(i13_dataset1, ensure_clean_memory):
133133
cor, overlap, side, overlap_position = find_center_360(data_normalised)
134134

135135
assert int(cor) == 2322
136+
assert int(overlap) == 473 # actual 473.822265625
136137
assert cor.dtype == np.float64
137138

138139

0 commit comments

Comments
 (0)