Skip to content

Commit 1563b95

Browse files
committed
adding more tests with zenodo, fixing CI, removing data
1 parent e55dec4 commit 1563b95

File tree

10 files changed

+235
-335
lines changed

10 files changed

+235
-335
lines changed

.scripts/download_zenodo.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ def calculate_md5(filename):
1919

2020
def download_zenodo_files(output_dir: Path):
2121
"""
22-
Download all files from Zenodo record 14627503 and verify their checksums.
22+
Download all files from Zenodo record 14652312 and verify their checksums.
2323
2424
Args:
2525
output_dir: Directory where files should be downloaded
2626
"""
2727
try:
28-
print("Fetching files from Zenodo record 14627503...")
28+
print("Fetching files from Zenodo record 14652312...")
2929
with urllib.request.urlopen(
30-
"https://zenodo.org/api/records/14627503"
30+
"https://zenodo.org/api/records/14652312"
3131
) as response:
3232
data = json.loads(response.read())
3333

docs/source/examples/Cor_largesino.ipynb

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

docs/source/index.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,4 @@
3030

3131
examples/pipeline1_FBP
3232
examples/pipeline2_iterative
33-
examples/Cor_largesino
34-
examples/DistortionCorr
33+
examples/DistortionCorr

httomolibgpu/recon/rotation.py

Lines changed: 14 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from cupyx.scipy.ndimage import shift, gaussian_filter
3434
from skimage.registration import phase_cross_correlation
3535
from cupyx.scipy.fftpack import get_fft_plan
36-
from cupyx.scipy.fft import rfft2, fft2, fftshift
36+
from cupyx.scipy.fft import fft2, fftshift
3737
else:
3838
load_cuda_module = Mock()
3939
shift = Mock()
@@ -58,7 +58,7 @@
5858
def find_center_vo(
5959
data: cp.ndarray,
6060
ind: Optional[int] = None,
61-
average_radius: Optional[int] = 0,
61+
average_radius: int = 0,
6262
cor_initialisation_value: Optional[float] = None,
6363
smin: int = -100,
6464
smax: int = 100,
@@ -77,24 +77,24 @@ def find_center_vo(
7777
3D [angles, detY, detX] tomographic data or a 2D [angles, detX] sinogram as a CuPy array.
7878
ind : int, optional
7979
Index of the slice to be used to estimate the CoR. If None is given, then the central sinogram will be extracted from the data array with a possible averaging, see .
80-
average_radius : int, optional
80+
average_radius : int
8181
Averaging multiple sinograms around the ind-indexed sinogram to improve the signal-to-noise ratio. It is recommended to keep this parameter smaller than 10.
8282
cor_initialisation_value : float, optional
8383
The initial approximation for the centre of rotation. If the value is None, use the horizontal centre of the projection/sinogram image.
84-
smin : int, optional
84+
smin : int
8585
Coarse search radius. Reference to the horizontal center of
8686
the sinogram.
87-
smax : int, optional
87+
smax : int
8888
Coarse search radius. Reference to the horizontal center of
8989
the sinogram.
90-
srad : float, optional
90+
srad : float
9191
Fine search radius.
92-
step : float, optional
92+
step : float
9393
Step of fine searching.
94-
ratio : float, optional
94+
ratio : float
9595
The ratio between the FOV of the camera and the size of object.
9696
It's used to generate the mask.
97-
drop : int, optional
97+
drop : int
9898
Drop lines around vertical center of the mask.
9999
100100
Returns
@@ -148,11 +148,8 @@ def find_center_vo(
148148
if dsp_angle > 1 or dsp_detX > 1:
149149
_sino_cs = _downsample(_sino_cs, dsp_angle, dsp_detX)
150150

151-
# NOTE: this is correct implementation that avoids running any CUDA kernels. The performance is suboptimal
152151
init_cen = _search_coarse(_sino_cs, start_cor, stop_cor, ratio, drop)
153152

154-
# NOTE: similar to the coarse module above, this is currently a correct function
155-
# but it is NOT using CUDA kernels written. Therefore some kernels re-writing is needed.
156153
fine_cen = _search_fine(
157154
_sino_fs, fine_srange, step, float(init_cen) * dsp_detX + off_set, ratio, drop
158155
)
@@ -218,53 +215,6 @@ def _search_fine(sino, srad, step, init_cen, ratio, drop):
218215
return cor
219216

220217

221-
def _create_mask_numpy(nrow, ncol, radius, drop):
222-
du = 1.0 / ncol
223-
dv = (nrow - 1.0) / (nrow * 2.0 * np.pi)
224-
cen_row = np.int16(np.ceil(nrow / 2.0) - 1)
225-
cen_col = np.int16(np.ceil(ncol / 2.0) - 1)
226-
drop = min(drop, np.int16(np.ceil(0.05 * nrow)))
227-
mask = np.zeros((nrow, ncol), dtype="float32")
228-
for i in range(nrow):
229-
pos = np.int16(np.round(((i - cen_row) * dv / radius) / du))
230-
(pos1, pos2) = np.clip(np.sort((-pos + cen_col, pos + cen_col)), 0, ncol - 1)
231-
mask[i, pos1 : pos2 + 1] = 1.0
232-
mask[cen_row - drop : cen_row + drop + 1, :] = 0.0
233-
mask[:, cen_col - 1 : cen_col + 2] = 0.0
234-
return mask
235-
236-
237-
def _create_mask_half(nrow, ncol, radius, drop):
238-
du = 1.0 / ncol
239-
dv = (nrow - 1.0) / (nrow * 2.0 * np.pi)
240-
cen_row = int(math.ceil(nrow / 2.0) - 1)
241-
cen_col = int(math.ceil(ncol / 2.0) - 1)
242-
drop = min([drop, int(math.ceil(0.05 * nrow))])
243-
244-
block_x = 128
245-
block_y = 1
246-
block_dims = (block_x, block_y)
247-
grid_x = (ncol // 2 + 1 + block_x - 1) // block_x
248-
grid_y = nrow
249-
grid_dims = (grid_x, grid_y)
250-
mask = cp.empty((nrow, ncol // 2 + 1), dtype="uint16")
251-
params = (
252-
ncol,
253-
nrow,
254-
cen_col,
255-
cen_row,
256-
cp.float32(du),
257-
cp.float32(dv),
258-
cp.float32(radius),
259-
cp.float32(drop),
260-
mask,
261-
)
262-
module = load_cuda_module("generate_mask")
263-
kernel = module.get_function("generate_mask")
264-
kernel(grid_dims, block_dims, params)
265-
return mask
266-
267-
268218
def _create_mask(nrow, ncol, radius, drop):
269219
du = 1.0 / ncol
270220
dv = (nrow - 1.0) / (nrow * 2.0 * np.pi)
@@ -321,12 +271,10 @@ def _calculate_chunks(
321271

322272
available_memory -= shift_size
323273
freq_domain_size = (
324-
# shift_size # it needs only half (RFFT), but complex64, so it's the same
325-
shift_size
326-
* 2 # it needs full (FFT), with complex64, so it's double
274+
shift_size * 2 # it needs full (FFT), with complex64, so it's double
327275
)
328276
fft_plan_size = freq_domain_size
329-
size_per_shift = 2 * (fft_plan_size + freq_domain_size + shift_size)
277+
size_per_shift = 2.5 * (fft_plan_size + freq_domain_size + shift_size)
330278
nshift_max = available_memory // size_per_shift
331279
assert nshift_max > 0, "Not enough memory to process"
332280
num_chunks = int(np.ceil(nshifts / nshift_max))
@@ -357,10 +305,8 @@ def _calculate_metric(list_shift, sino, flip_sino, comp_sino, mask, out):
357305
# The sum is enough.
358306
masked_sum_abs_kernel = cp.ReductionKernel(
359307
in_params="complex64 x, float32 mask", # input, complex + mask
360-
# in_params="complex64 x, uint16 mask", # input, complex + mask
361308
out_params="float32 out", # output, real
362309
map_expr="abs(x) * mask",
363-
# map_expr="mask ? abs(x) : 0.0f",
364310
reduce_expr="a + b",
365311
post_map_expr="out = a",
366312
identity="0.0f",
@@ -421,7 +367,6 @@ def _calculate_metric(list_shift, sino, flip_sino, comp_sino, mask, out):
421367
# stack and transform
422368
# (we do the full sized mat FFT, even though the last chunk may be smaller, to
423369
# make sure we can re-use the same FFT plan as before)
424-
# mat_freq = fft2(mat, axes=(1, 2), norm=None, plan=plan)
425370
mat_freq = fftshift(fft2(mat, axes=(1, 2), norm=None, plan=plan), axes=(1, 2))
426371

427372
masked_sum_abs_kernel(
@@ -789,7 +734,7 @@ def find_center_pc(
789734
proj2: cp.ndarray,
790735
tol: float = 0.5,
791736
rotc_guess: Union[float, Optional[str]] = None,
792-
) -> float:
737+
) -> np.float32:
793738
"""
794739
Find rotation axis location by finding the offset between the first
795740
projection and a mirrored projection 180 degrees apart using
@@ -811,7 +756,7 @@ def find_center_pc(
811756
812757
Returns
813758
----------
814-
float
759+
np.float32
815760
Rotation axis location.
816761
"""
817762
imgshift = 0.0 if rotc_guess is None else rotc_guess - (proj1.shape[1] - 1.0) / 2.0
@@ -831,7 +776,7 @@ def find_center_pc(
831776
# registered translation with the second image
832777
center = (proj1.shape[1] + shiftr[0][1] - 1.0) / 2.0
833778

834-
return center + imgshift
779+
return np.float32(center + imgshift)
835780

836781

837782
##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

tests/conftest.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,6 @@ def ensure_clean_memory():
6565
cp.get_default_pinned_memory_pool().free_all_blocks()
6666

6767

68-
@pytest.fixture(scope="session")
69-
def sino3600_file(test_data_path):
70-
in_file = os.path.join(test_data_path, "3600proj_sino.npz")
71-
return np.load(in_file)
72-
73-
74-
@pytest.fixture
75-
def sino3600(sino3600_file):
76-
return cp.asarray(sino3600_file["sinogram"])
77-
78-
7968
@pytest.fixture
8069
def host_data(data_file):
8170
return np.copy(data_file["data"])

tests/test_data/3600proj_sino.npz

-35.2 MB
Binary file not shown.

tests/test_misc/test_morph.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@
77
from httomolibgpu.misc.morph import sino_360_to_180, data_resampler
88

99

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
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
2323

2424

2525
@pytest.mark.parametrize(

tests/test_recon/test_rotation.py

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,42 +47,30 @@ def test_find_center_vo_random(ensure_clean_memory):
4747
assert_allclose(cent, 550.25)
4848

4949

50-
def test_find_center_vo_big_data(sino3600):
51-
cent = find_center_vo(sino3600)
52-
assert_allclose(cent, 1333.25)
53-
54-
5550
def test_find_center_vo_calculate_chunks():
5651
# we need the split to fit into the available memory, and also make sure
5752
# that the last chunk is either the same or smaller than the previous ones
5853
# (so that we can re-use the same memory as for the previous chunks, incl. FFT plan)
5954
# Note: With shift_size = 100 bytes, we need 600 bytes per shift
6055
bytes_per_shift = 600
6156
assert _calculate_chunks(10, 100, 1000000) == [10]
62-
assert _calculate_chunks(10, 100, 10 * bytes_per_shift + 100) == [5, 10]
63-
assert _calculate_chunks(10, 100, 5 * bytes_per_shift + 100) == [3, 6, 9, 10]
64-
assert _calculate_chunks(10, 100, 7 * bytes_per_shift + 100) == [4, 8, 10]
65-
assert _calculate_chunks(10, 100, 9 * bytes_per_shift + 100) == [5, 10]
66-
assert _calculate_chunks(9, 100, 5 * bytes_per_shift + 100) == [3, 6, 9]
67-
assert _calculate_chunks(10, 100, 4 * bytes_per_shift + 100) == [2, 4, 6, 8, 10]
68-
# add a bit of randomness here, to check basic assumptions
69-
# random.seed(123456)
70-
# for _ in range(100):
71-
# available = random.randint(
72-
# 1 * bytes_per_shift + 100, 100 * bytes_per_shift + 100
73-
# ) # memory to fit anywhere between 1 and 100 shifts
74-
# nshifts = random.randint(1, 1000)
75-
# chunks = _calculate_chunks(nshifts, 100, available)
76-
# assert len(chunks) > 0
77-
# assert len(chunks) == math.ceil(
78-
# nshifts / ((available - 100) // bytes_per_shift)
79-
# )
80-
# assert chunks[-1] == nshifts
81-
# if len(chunks) > 1:
82-
# diffs = np.diff(chunks)
83-
# assert diffs[0] > 0
84-
# np.testing.assert_array_equal(diffs[:-1], diffs[0])
85-
# assert diffs[-1] <= diffs[0]
57+
assert _calculate_chunks(10, 100, 10 * bytes_per_shift + 100) == [4, 8, 10]
58+
assert _calculate_chunks(10, 100, 5 * bytes_per_shift + 100) == [2, 4, 6, 8, 10]
59+
assert _calculate_chunks(10, 100, 7 * bytes_per_shift + 100) == [3, 6, 9, 10]
60+
assert _calculate_chunks(10, 100, 9 * bytes_per_shift + 100) == [4, 8, 10]
61+
assert _calculate_chunks(9, 100, 5 * bytes_per_shift + 100) == [2, 4, 6, 8, 9]
62+
assert _calculate_chunks(10, 100, 4 * bytes_per_shift + 100) == [
63+
1,
64+
2,
65+
3,
66+
4,
67+
5,
68+
6,
69+
7,
70+
8,
71+
9,
72+
10,
73+
]
8674

8775

8876
@pytest.mark.perf

0 commit comments

Comments
 (0)