Skip to content

Commit 0df1200

Browse files
authored
Merge pull request #200 from DiamondLightSource/zenodo_stripes
Zenodo stripes removal tests
2 parents 0d1f8fc + b87879e commit 0df1200

File tree

10 files changed

+283
-89
lines changed

10 files changed

+283
-89
lines changed

.scripts/download_zenodo.py

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

1919
def download_zenodo_files(output_dir: Path):
2020
"""
21-
Download all files from Zenodo record 14833590 and verify their checksums.
21+
Download all files from Zenodo record 14938787 and verify their checksums.
2222
2323
Args:
2424
output_dir: Directory where files should be downloaded
2525
"""
2626
try:
27-
print("Fetching files from Zenodo record 14833590...")
27+
print("Fetching files from Zenodo record 14938787...")
2828
with urllib.request.urlopen(
29-
"https://zenodo.org/api/records/14833590"
29+
"https://zenodo.org/api/records/14938787"
3030
) as response:
3131
data = json.loads(response.read())
3232

httomolibgpu/misc/morph.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def data_resampler(
112112
113113
Parameters
114114
----------
115-
data : cp.ndarray
115+
data : cp.ndarray
116116
3d cupy array.
117117
newshape : list
118118
2d list that defines the 2D slice shape of new shape data.

httomolibgpu/prep/stripe.py

Lines changed: 11 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -205,40 +205,12 @@ def remove_all_stripe(
205205
for m in range(data.shape[1]):
206206
sino = data[:, m, :]
207207
sino = _rs_dead(sino, snr, la_size, matindex)
208-
sino = _rs_sort2(sino, sm_size, matindex, dim)
208+
sino = _rs_sort(sino, sm_size, dim)
209209
sino = cp.nan_to_num(sino)
210210
data[:, m, :] = sino
211211
return data
212212

213213

214-
def _rs_sort2(sinogram, size, matindex, dim):
215-
"""
216-
Remove stripes using the sorting technique.
217-
"""
218-
sinogram = cp.transpose(sinogram)
219-
matcomb = cp.asarray(cp.dstack((matindex, sinogram)))
220-
221-
# matsort = cp.asarray([row[row[:, 1].argsort()] for row in matcomb])
222-
ids = cp.argsort(matcomb[:, :, 1], axis=1)
223-
matsort = matcomb.copy()
224-
matsort[:, :, 0] = cp.take_along_axis(matsort[:, :, 0], ids, axis=1)
225-
matsort[:, :, 1] = cp.take_along_axis(matsort[:, :, 1], ids, axis=1)
226-
if dim == 1:
227-
matsort[:, :, 1] = median_filter(matsort[:, :, 1], (size, 1))
228-
else:
229-
matsort[:, :, 1] = median_filter(matsort[:, :, 1], (size, size))
230-
231-
# matsortback = cp.asarray([row[row[:, 0].argsort()] for row in matsort])
232-
233-
ids = cp.argsort(matsort[:, :, 0], axis=1)
234-
matsortback = matsort.copy()
235-
matsortback[:, :, 0] = cp.take_along_axis(matsortback[:, :, 0], ids, axis=1)
236-
matsortback[:, :, 1] = cp.take_along_axis(matsortback[:, :, 1], ids, axis=1)
237-
238-
sino_corrected = matsortback[:, :, 1]
239-
return cp.transpose(sino_corrected)
240-
241-
242214
def _mpolyfit(x, y):
243215
n = len(x)
244216
x_mean = cp.mean(x)
@@ -261,8 +233,6 @@ def _detect_stripe(listdata, snr):
261233
listsorted = cp.sort(listdata)[::-1]
262234
xlist = cp.arange(0, numdata, 1.0)
263235
ndrop = cp.int16(0.25 * numdata)
264-
# (_slope, _intercept) = cp.polyfit(xlist[ndrop:-ndrop - 1],
265-
# listsorted[ndrop:-ndrop - 1], 1)
266236
(_slope, _intercept) = _mpolyfit(
267237
xlist[ndrop : -ndrop - 1], listsorted[ndrop : -ndrop - 1]
268238
)
@@ -293,11 +263,6 @@ def _rs_large(sinogram, snr, size, matindex, drop_ratio=0.1, norm=True):
293263
sinosmooth = median_filter(sinosort, (1, size))
294264
list1 = cp.mean(sinosort[ndrop : nrow - ndrop], axis=0)
295265
list2 = cp.mean(sinosmooth[ndrop : nrow - ndrop], axis=0)
296-
# listfact = cp.divide(list1,
297-
# list2,
298-
# out=cp.ones_like(list1),
299-
# where=list2 != 0)
300-
301266
listfact = list1 / list2
302267

303268
# Locate stripes
@@ -310,14 +275,12 @@ def _rs_large(sinogram, snr, size, matindex, drop_ratio=0.1, norm=True):
310275
sinogram1 = cp.transpose(sinogram)
311276
matcombine = cp.asarray(cp.dstack((matindex, sinogram1)))
312277

313-
# matsort = cp.asarray([row[row[:, 1].argsort()] for row in matcombine])
314278
ids = cp.argsort(matcombine[:, :, 1], axis=1)
315279
matsort = matcombine.copy()
316280
matsort[:, :, 0] = cp.take_along_axis(matsort[:, :, 0], ids, axis=1)
317281
matsort[:, :, 1] = cp.take_along_axis(matsort[:, :, 1], ids, axis=1)
318282

319283
matsort[:, :, 1] = cp.transpose(sinosmooth)
320-
# matsortback = cp.asarray([row[row[:, 0].argsort()] for row in matsort])
321284
ids = cp.argsort(matsort[:, :, 0], axis=1)
322285
matsortback = matsort.copy()
323286
matsortback[:, :, 0] = cp.take_along_axis(matsortback[:, :, 0], ids, axis=1)
@@ -330,12 +293,9 @@ def _rs_large(sinogram, snr, size, matindex, drop_ratio=0.1, norm=True):
330293

331294

332295
def _rs_dead(sinogram, snr, size, matindex, norm=True):
333-
"""
334-
Remove unresponsive and fluctuating stripes.
335-
"""
296+
"""remove unresponsive and fluctuating stripes"""
336297
sinogram = cp.copy(sinogram) # Make it mutable
337298
(nrow, _) = sinogram.shape
338-
# sinosmooth = cp.apply_along_axis(uniform_filter1d, 0, sinogram, 10)
339299
sinosmooth = uniform_filter1d(sinogram, 10, axis=0)
340300

341301
listdiff = cp.sum(cp.abs(sinogram - sinosmooth), axis=0)
@@ -344,22 +304,22 @@ def _rs_dead(sinogram, snr, size, matindex, norm=True):
344304
listfact = listdiff / listdiffbck
345305

346306
listmask = _detect_stripe(listfact, snr)
307+
del listfact
347308
listmask = binary_dilation(listmask, iterations=1).astype(listmask.dtype)
348309
listmask[0:2] = 0.0
349310
listmask[-2:] = 0.0
350-
listx = cp.where(listmask < 1.0)[0]
351-
listy = cp.arange(nrow)
352-
matz = sinogram[:, listx]
353311

312+
listx = cp.where(listmask < 1.0)[0]
354313
listxmiss = cp.where(listmask > 0.0)[0]
314+
del listmask
355315

356-
# finter = interpolate.interp2d(listx.get(), listy.get(), matz.get(), kind='linear')
357316
if len(listxmiss) > 0:
358-
# sinogram_c[:, listxmiss.get()] = finter(listxmiss.get(), listy.get())
359317
ids = cp.searchsorted(listx, listxmiss)
360-
sinogram[:, listxmiss] = matz[:, ids - 1] + (listxmiss - listx[ids - 1]) * (
361-
matz[:, ids] - matz[:, ids - 1]
362-
) / (listx[ids] - listx[ids - 1])
318+
weights = (listxmiss - listx[ids - 1]) / (listx[ids] - listx[ids - 1])
319+
# direct interpolation without making an extra copy
320+
sinogram[:, listxmiss] = sinogram[:, listx[ids - 1]] + weights * (
321+
sinogram[:, listx[ids]] - sinogram[:, listx[ids - 1]]
322+
)
363323

364324
# Remove residual stripes
365325
if norm is True:
@@ -455,7 +415,7 @@ def raven_filter(
455415
# Removing padding
456416
data = data[pad_y : height - pad_y, :, pad_x : width - pad_x].real
457417

458-
return data
418+
return cp.require(data, requirements="C")
459419

460420

461421
def _create_matindex(nrow, ncol):

httomolibgpu/recon/algorithm.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ def FBP(
8383
out some undesirable artifacts. The values outside the given diameter will be set to zero.
8484
It is recommended to keep the value in the range [0.7-1.0].
8585
neglog: bool
86-
Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
87-
assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
86+
Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
87+
assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
8888
gpu_id : int
8989
A GPU device index to perform operation on.
9090
@@ -137,8 +137,8 @@ def LPRec(
137137
out some undesirable artifacts. The values outside the given diameter will be set to zero.
138138
It is recommended to keep the value in the range [0.7-1.0].
139139
neglog: bool
140-
Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
141-
assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
140+
Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
141+
assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
142142
143143
Returns
144144
-------
@@ -189,8 +189,8 @@ def SIRT(
189189
nonnegativity : bool, optional
190190
Impose nonnegativity constraint on reconstructed image.
191191
neglog: bool
192-
Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
193-
assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
192+
Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
193+
assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
194194
gpu_id : int, optional
195195
A GPU device index to perform operation on.
196196
@@ -254,8 +254,8 @@ def CGLS(
254254
nonnegativity : bool, optional
255255
Impose nonnegativity constraint on reconstructed image.
256256
neglog: bool
257-
Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
258-
assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
257+
Take negative logarithm on input data to convert to attenuation coefficient or a density of the scanned object. Defaults to False,
258+
assuming that the negative log is taken either in normalisation procedure on with Paganin filter application.
259259
gpu_id : int, optional
260260
A GPU device index to perform operation on.
261261
@@ -277,6 +277,7 @@ def CGLS(
277277
cp._default_memory_pool.free_all_blocks()
278278
return cp.require(cp.swapaxes(reconstruction, 0, 1), requirements="C")
279279

280+
280281
## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ##
281282
def _instantiate_direct_recon_class(
282283
data: cp.ndarray,
@@ -352,10 +353,11 @@ def _instantiate_iterative_recon_class(
352353
)
353354
return RecToolsCP
354355

356+
355357
def _take_neg_log(data: cp.ndarray) -> cp.ndarray:
356358
"""Taking negative log"""
357-
data[data<=0] = 1
359+
data[data <= 0] = 1
358360
data = -cp.log(data)
359361
data[cp.isnan(data)] = 6.0
360362
data[cp.isinf(data)] = 0
361-
return data
363+
return data

tests/test_misc/test_morph.py

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

9+
910
@pytest.mark.parametrize(
1011
"overlap, rotation",
1112
[

tests/test_prep/stripe_cpu_reference.py

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,29 @@
22
import pyfftw
33
import pyfftw.interfaces.numpy_fft as fft
44

5+
56
def raven_filter_numpy(
6-
sinogram,
7-
uvalue: int = 20,
8-
nvalue: int = 4,
9-
vvalue: int = 2,
10-
pad_y: int = 20,
11-
pad_x: int = 20,
12-
pad_method: str = "edge"):
13-
7+
sinogram,
8+
uvalue: int = 20,
9+
nvalue: int = 4,
10+
vvalue: int = 2,
11+
pad_y: int = 20,
12+
pad_x: int = 20,
13+
pad_method: str = "edge",
14+
):
1415
# Parameters
1516
v0 = vvalue
1617
n = nvalue
1718
u0 = uvalue
1819

1920
# Make a padded copy
20-
sinogram_padded = np.pad(sinogram, ((pad_y,pad_y), (0, 0), (pad_x,pad_x)), pad_method)
21-
21+
sinogram_padded = np.pad(
22+
sinogram, ((pad_y, pad_y), (0, 0), (pad_x, pad_x)), pad_method
23+
)
24+
2225
# Size
2326
height, images, width = sinogram_padded.shape
24-
27+
2528
# Generate filter function
2629
centerx = np.ceil(width / 2.0) - 1.0
2730
centery = np.int16(np.ceil(height / 2.0) - 1)
@@ -32,19 +35,19 @@ def raven_filter_numpy(
3235
filtershapepad2d = np.zeros((row2 - row1, filtershape.size))
3336
filtershapepad2d[:] = np.float64(filtershape)
3437
filtercomplex = filtershapepad2d + filtershapepad2d * 1j
35-
38+
3639
# Generate filter objects
37-
a = pyfftw.empty_aligned((height, images, width), dtype='complex128', n=16)
38-
b = pyfftw.empty_aligned((height, images, width), dtype='complex128', n=16)
39-
c = pyfftw.empty_aligned((height, images, width), dtype='complex128', n=16)
40-
d = pyfftw.empty_aligned((height, images, width), dtype='complex128', n=16)
41-
fft_object = pyfftw.FFTW(a, b, axes=(0, 2))
42-
ifft_object = pyfftw.FFTW(c, d, axes=(0, 2), direction='FFTW_BACKWARD')
43-
40+
a = pyfftw.empty_aligned((height, images, width), dtype="complex128", n=16)
41+
b = pyfftw.empty_aligned((height, images, width), dtype="complex128", n=16)
42+
c = pyfftw.empty_aligned((height, images, width), dtype="complex128", n=16)
43+
d = pyfftw.empty_aligned((height, images, width), dtype="complex128", n=16)
44+
fft_object = pyfftw.FFTW(a, b, axes=(0, 2))
45+
ifft_object = pyfftw.FFTW(c, d, axes=(0, 2), direction="FFTW_BACKWARD")
46+
4447
sino = fft.fftshift(fft_object(sinogram_padded), axes=(0, 2))
4548
for m in range(sino.shape[1]):
4649
sino[row1:row2, m] = sino[row1:row2, m] * filtercomplex
4750
sino = ifft_object(fft.ifftshift(sino, axes=(0, 2)))
48-
sinogram = sino[pad_y:height-pad_y, :, pad_x:width-pad_x]
51+
sinogram = sino[pad_y : height - pad_y, :, pad_x : width - pad_x]
4952

50-
return sinogram.real
53+
return sinogram.real

0 commit comments

Comments
 (0)