nabu 2023.2.1__py3-none-any.whl → 2024.1.0rc3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- doc/conf.py +1 -1
- doc/doc_config.py +32 -0
- nabu/__init__.py +2 -1
- nabu/app/bootstrap_stitching.py +1 -1
- nabu/app/cli_configs.py +122 -2
- nabu/app/composite_cor.py +27 -2
- nabu/app/correct_rot.py +70 -0
- nabu/app/create_distortion_map_from_poly.py +42 -18
- nabu/app/diag_to_pix.py +358 -0
- nabu/app/diag_to_rot.py +449 -0
- nabu/app/generate_header.py +4 -3
- nabu/app/histogram.py +2 -2
- nabu/app/multicor.py +6 -1
- nabu/app/parse_reconstruction_log.py +151 -0
- nabu/app/prepare_weights_double.py +83 -22
- nabu/app/reconstruct.py +5 -1
- nabu/app/reconstruct_helical.py +7 -0
- nabu/app/reduce_dark_flat.py +6 -3
- nabu/app/rotate.py +4 -4
- nabu/app/stitching.py +16 -2
- nabu/app/tests/test_reduce_dark_flat.py +18 -2
- nabu/app/validator.py +4 -4
- nabu/cuda/convolution.py +8 -376
- nabu/cuda/fft.py +4 -0
- nabu/cuda/kernel.py +4 -4
- nabu/cuda/medfilt.py +5 -158
- nabu/cuda/padding.py +5 -71
- nabu/cuda/processing.py +23 -2
- nabu/cuda/src/ElementOp.cu +78 -0
- nabu/cuda/src/backproj.cu +28 -2
- nabu/cuda/src/fourier_wavelets.cu +2 -2
- nabu/cuda/src/normalization.cu +23 -0
- nabu/cuda/src/padding.cu +2 -2
- nabu/cuda/src/transpose.cu +16 -0
- nabu/cuda/utils.py +39 -0
- nabu/estimation/alignment.py +10 -1
- nabu/estimation/cor.py +808 -38
- nabu/estimation/cor_sino.py +7 -9
- nabu/estimation/tests/test_cor.py +85 -3
- nabu/io/reader.py +26 -18
- nabu/io/tests/test_cast_volume.py +3 -3
- nabu/io/tests/test_detector_distortion.py +3 -3
- nabu/io/tiffwriter_zmm.py +2 -2
- nabu/io/utils.py +14 -4
- nabu/io/writer.py +5 -3
- nabu/misc/fftshift.py +6 -0
- nabu/misc/histogram.py +5 -285
- nabu/misc/histogram_cuda.py +8 -104
- nabu/misc/kernel_base.py +3 -121
- nabu/misc/padding_base.py +5 -69
- nabu/misc/processing_base.py +3 -107
- nabu/misc/rotation.py +5 -62
- nabu/misc/rotation_cuda.py +5 -65
- nabu/misc/transpose.py +6 -0
- nabu/misc/unsharp.py +3 -78
- nabu/misc/unsharp_cuda.py +5 -52
- nabu/misc/unsharp_opencl.py +8 -85
- nabu/opencl/fft.py +6 -0
- nabu/opencl/kernel.py +21 -6
- nabu/opencl/padding.py +5 -72
- nabu/opencl/processing.py +27 -5
- nabu/opencl/src/backproj.cl +3 -3
- nabu/opencl/src/fftshift.cl +65 -12
- nabu/opencl/src/padding.cl +2 -2
- nabu/opencl/src/roll.cl +96 -0
- nabu/opencl/src/transpose.cl +16 -0
- nabu/pipeline/config_validators.py +63 -3
- nabu/pipeline/dataset_validator.py +2 -2
- nabu/pipeline/estimators.py +193 -35
- nabu/pipeline/fullfield/chunked.py +34 -17
- nabu/pipeline/fullfield/chunked_cuda.py +7 -5
- nabu/pipeline/fullfield/computations.py +48 -13
- nabu/pipeline/fullfield/nabu_config.py +13 -13
- nabu/pipeline/fullfield/processconfig.py +10 -5
- nabu/pipeline/fullfield/reconstruction.py +1 -2
- nabu/pipeline/helical/fbp.py +5 -0
- nabu/pipeline/helical/filtering.py +12 -9
- nabu/pipeline/helical/gridded_accumulator.py +179 -33
- nabu/pipeline/helical/helical_chunked_regridded.py +262 -151
- nabu/pipeline/helical/helical_chunked_regridded_cuda.py +4 -11
- nabu/pipeline/helical/helical_reconstruction.py +56 -18
- nabu/pipeline/helical/span_strategy.py +1 -1
- nabu/pipeline/helical/tests/test_accumulator.py +4 -0
- nabu/pipeline/params.py +23 -2
- nabu/pipeline/processconfig.py +3 -8
- nabu/pipeline/tests/test_chunk_reader.py +78 -0
- nabu/pipeline/tests/test_estimators.py +120 -2
- nabu/pipeline/utils.py +25 -0
- nabu/pipeline/writer.py +2 -0
- nabu/preproc/ccd_cuda.py +9 -7
- nabu/preproc/ctf.py +21 -26
- nabu/preproc/ctf_cuda.py +25 -25
- nabu/preproc/double_flatfield.py +14 -2
- nabu/preproc/double_flatfield_cuda.py +7 -11
- nabu/preproc/flatfield_cuda.py +23 -27
- nabu/preproc/phase.py +19 -24
- nabu/preproc/phase_cuda.py +21 -21
- nabu/preproc/shift_cuda.py +58 -28
- nabu/preproc/tests/test_ctf.py +5 -5
- nabu/preproc/tests/test_double_flatfield.py +2 -2
- nabu/preproc/tests/test_vshift.py +13 -2
- nabu/processing/__init__.py +0 -0
- nabu/processing/convolution_cuda.py +375 -0
- nabu/processing/fft_base.py +163 -0
- nabu/processing/fft_cuda.py +256 -0
- nabu/processing/fft_opencl.py +54 -0
- nabu/processing/fftshift.py +134 -0
- nabu/processing/histogram.py +286 -0
- nabu/processing/histogram_cuda.py +103 -0
- nabu/processing/kernel_base.py +126 -0
- nabu/processing/medfilt_cuda.py +159 -0
- nabu/processing/muladd.py +29 -0
- nabu/processing/muladd_cuda.py +68 -0
- nabu/processing/padding_base.py +71 -0
- nabu/processing/padding_cuda.py +75 -0
- nabu/processing/padding_opencl.py +77 -0
- nabu/processing/processing_base.py +123 -0
- nabu/processing/roll_opencl.py +64 -0
- nabu/processing/rotation.py +63 -0
- nabu/processing/rotation_cuda.py +66 -0
- nabu/processing/tests/__init__.py +0 -0
- nabu/processing/tests/test_fft.py +268 -0
- nabu/processing/tests/test_fftshift.py +71 -0
- nabu/{misc → processing}/tests/test_histogram.py +2 -4
- nabu/{cuda → processing}/tests/test_medfilt.py +1 -1
- nabu/processing/tests/test_muladd.py +54 -0
- nabu/{cuda → processing}/tests/test_padding.py +119 -75
- nabu/processing/tests/test_roll.py +63 -0
- nabu/{misc → processing}/tests/test_rotation.py +3 -2
- nabu/processing/tests/test_transpose.py +72 -0
- nabu/{misc → processing}/tests/test_unsharp.py +41 -8
- nabu/processing/transpose.py +126 -0
- nabu/processing/unsharp.py +79 -0
- nabu/processing/unsharp_cuda.py +53 -0
- nabu/processing/unsharp_opencl.py +75 -0
- nabu/reconstruction/fbp.py +34 -10
- nabu/reconstruction/fbp_base.py +35 -16
- nabu/reconstruction/fbp_opencl.py +7 -12
- nabu/reconstruction/filtering.py +2 -2
- nabu/reconstruction/filtering_cuda.py +13 -14
- nabu/reconstruction/filtering_opencl.py +3 -4
- nabu/reconstruction/projection.py +2 -0
- nabu/reconstruction/rings.py +158 -1
- nabu/reconstruction/rings_cuda.py +218 -58
- nabu/reconstruction/sinogram_cuda.py +16 -12
- nabu/reconstruction/tests/test_deringer.py +116 -14
- nabu/reconstruction/tests/test_fbp.py +22 -31
- nabu/reconstruction/tests/test_filtering.py +11 -2
- nabu/resources/dataset_analyzer.py +89 -26
- nabu/resources/nxflatfield.py +2 -2
- nabu/resources/tests/test_nxflatfield.py +1 -1
- nabu/resources/utils.py +9 -2
- nabu/stitching/alignment.py +184 -0
- nabu/stitching/config.py +241 -39
- nabu/stitching/definitions.py +6 -0
- nabu/stitching/frame_composition.py +4 -2
- nabu/stitching/overlap.py +99 -3
- nabu/stitching/sample_normalization.py +60 -0
- nabu/stitching/slurm_utils.py +10 -10
- nabu/stitching/tests/test_alignment.py +99 -0
- nabu/stitching/tests/test_config.py +16 -1
- nabu/stitching/tests/test_overlap.py +68 -2
- nabu/stitching/tests/test_sample_normalization.py +49 -0
- nabu/stitching/tests/test_slurm_utils.py +5 -5
- nabu/stitching/tests/test_utils.py +3 -33
- nabu/stitching/tests/test_z_stitching.py +391 -22
- nabu/stitching/utils.py +144 -202
- nabu/stitching/z_stitching.py +309 -126
- nabu/testutils.py +18 -0
- nabu/thirdparty/tomocupy_remove_stripe.py +586 -0
- nabu/utils.py +32 -6
- {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/LICENSE +1 -1
- {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/METADATA +5 -5
- nabu-2024.1.0rc3.dist-info/RECORD +296 -0
- {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/WHEEL +1 -1
- {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/entry_points.txt +5 -1
- nabu/conftest.py +0 -14
- nabu/opencl/fftshift.py +0 -92
- nabu/opencl/tests/test_fftshift.py +0 -55
- nabu/opencl/tests/test_padding.py +0 -84
- nabu-2023.2.1.dist-info/RECORD +0 -252
- /nabu/cuda/src/{fftshift.cu → dfi_fftshift.cu} +0 -0
- {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/top_level.txt +0 -0
nabu/reconstruction/fbp_base.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import numpy as np
|
2
2
|
from ..utils import updiv, nextpow2, convert_index, deprecation_warning
|
3
|
-
from ..
|
3
|
+
from ..processing.processing_base import ProcessingBase
|
4
4
|
from .filtering import SinoFilter
|
5
5
|
from .sinogram import SinoMult
|
6
6
|
from .sinogram import get_extended_sinogram_width
|
@@ -107,6 +107,7 @@ class BackprojectorBase:
|
|
107
107
|
"""
|
108
108
|
self._processing = self.backend_processing_class(**(backend_options or {}))
|
109
109
|
self._configure_extra_options(scale_factor, padding_mode, extra_options=extra_options)
|
110
|
+
self._check_textures_availability()
|
110
111
|
self._init_geometry(sino_shape, slice_shape, angles, rot_center, halftomo, slice_roi)
|
111
112
|
self._init_filter(filter_name)
|
112
113
|
self._allocate_memory()
|
@@ -163,8 +164,8 @@ class BackprojectorBase:
|
|
163
164
|
# move = 0 if not(centered_axis) else start + (n-1)/2. - c
|
164
165
|
if self.extra_options["centered_axis"]:
|
165
166
|
self.offsets = {
|
166
|
-
"x":
|
167
|
-
"y":
|
167
|
+
"x": self.rot_center - (self.n_x - 1) / 2.0,
|
168
|
+
"y": self.rot_center - (self.n_y - 1) / 2.0,
|
168
169
|
}
|
169
170
|
#
|
170
171
|
self._set_axis_corr()
|
@@ -214,8 +215,8 @@ class BackprojectorBase:
|
|
214
215
|
# 1D textures are not supported in pyopencl
|
215
216
|
self.h_msin = np.zeros((1, self.n_angles), "f")
|
216
217
|
self.h_cos = np.zeros((1, self.n_angles), "f")
|
217
|
-
self._d_sino = self._processing.allocate_array("d_sino", self.sino_shape, "f")
|
218
|
-
self._processing.init_arrays_to_none(["_d_slice"])
|
218
|
+
# self._d_sino = self._processing.allocate_array("d_sino", self.sino_shape, "f")
|
219
|
+
self._processing.init_arrays_to_none(["_d_slice", "d_sino"])
|
219
220
|
|
220
221
|
def _compute_angles(self):
|
221
222
|
self.h_cos[0] = np.cos(self.angles).astype("f")
|
@@ -262,14 +263,19 @@ class BackprojectorBase:
|
|
262
263
|
"""
|
263
264
|
self.rot_center = rot_center
|
264
265
|
self.axis_pos = rot_center
|
265
|
-
|
266
|
+
# See kernels signature of backproj.cu and backproj.cl.
|
267
|
+
# The ifdef makes things a bit more complicated
|
268
|
+
proj_arg_idx = 4
|
269
|
+
if self.backend == "cuda" and self._use_textures:
|
270
|
+
proj_arg_idx = 3
|
271
|
+
self.kern_proj_args[proj_arg_idx] = rot_center
|
266
272
|
if self.extra_options["centered_axis"]:
|
267
273
|
self.offsets = {
|
268
|
-
"x":
|
269
|
-
"y":
|
274
|
+
"x": self.rot_center - (self.n_x - 1) / 2.0,
|
275
|
+
"y": self.rot_center - (self.n_y - 1) / 2.0,
|
270
276
|
}
|
271
|
-
self.kern_proj_args[
|
272
|
-
self.kern_proj_args[
|
277
|
+
self.kern_proj_args[proj_arg_idx + 3] = self.offsets["x"]
|
278
|
+
self.kern_proj_args[proj_arg_idx + 4] = self.offsets["y"]
|
273
279
|
|
274
280
|
# Try to factorize some code between Cuda and OpenCL
|
275
281
|
# Not ideal, as cuda uses "grid" = n_blocks_launched,
|
@@ -306,13 +312,14 @@ class BackprojectorBase:
|
|
306
312
|
self._get_kernel_options()
|
307
313
|
self.kern_proj_args = [
|
308
314
|
None, # output d_slice holder
|
315
|
+
None, # placeholder for sino (OpenCL or Cuda+no-texture)
|
309
316
|
np.int32(self.n_angles),
|
310
317
|
np.int32(self.dwidth),
|
311
318
|
np.float32(self.axis_pos),
|
312
319
|
np.int32(self.n_x),
|
313
320
|
np.int32(self.n_y),
|
314
|
-
np.
|
315
|
-
np.
|
321
|
+
np.float32(self.offsets["x"]),
|
322
|
+
np.float32(self.offsets["y"]),
|
316
323
|
self._d_cos,
|
317
324
|
self._d_msin,
|
318
325
|
np.float32(self._backproj_scale_factor),
|
@@ -353,16 +360,28 @@ class BackprojectorBase:
|
|
353
360
|
return self._processing._d_slice.get(ary=output)
|
354
361
|
|
355
362
|
def filtered_backprojection(self, sino, output=None):
|
363
|
+
#
|
364
|
+
if isinstance(sino, self._processing.array_class):
|
365
|
+
d_sino = sino
|
366
|
+
else:
|
367
|
+
d_sino = self._processing.to_device("d_sino", sino)
|
368
|
+
#
|
356
369
|
if self.sino_filter is not None:
|
357
|
-
|
370
|
+
filt_kwargs = {}
|
371
|
+
# if a new device array was allocated for sinogram, then the filtering can overwrite it,
|
372
|
+
# since it won't affect user argument
|
373
|
+
if id(d_sino) != id(sino):
|
374
|
+
filt_kwargs = {"output": d_sino}
|
375
|
+
#
|
376
|
+
sino_to_backproject = self.sino_filter(d_sino, **filt_kwargs)
|
358
377
|
else:
|
359
|
-
|
360
|
-
return self.backproj(
|
378
|
+
sino_to_backproject = d_sino
|
379
|
+
return self.backproj(sino_to_backproject, output=output)
|
361
380
|
|
362
381
|
fbp = filtered_backprojection # shorthand
|
363
382
|
|
364
383
|
def __repr__(self):
|
365
|
-
res = "%s(sino_shape=%s, slice_shape=%s, rot_center
|
384
|
+
res = "%s(sino_shape=%s, slice_shape=%s, rot_center=%.2f, halftomo=%s)" % (
|
366
385
|
self.__class__.__name__,
|
367
386
|
str(self.sino_shape),
|
368
387
|
str(self.slice_shape),
|
@@ -16,13 +16,10 @@ class OpenCLBackprojector(BackprojectorBase):
|
|
16
16
|
SinoFilterClass = OpenCLSinoFilter
|
17
17
|
SinoMultClass = OpenCLSinoMult
|
18
18
|
|
19
|
-
def
|
20
|
-
""
|
21
|
-
|
22
|
-
|
23
|
-
bp = self.backprojector
|
24
|
-
bp.axis_pos = rot_center
|
25
|
-
bp.kern_proj_args[2] = rot_center
|
19
|
+
def _check_textures_availability(self):
|
20
|
+
self._use_textures = self.extra_options.get("use_textures", True) and check_textures_availability(
|
21
|
+
self._processing.ctx
|
22
|
+
)
|
26
23
|
|
27
24
|
def _get_kernel_options(self):
|
28
25
|
super()._get_kernel_options()
|
@@ -48,15 +45,13 @@ class OpenCLBackprojector(BackprojectorBase):
|
|
48
45
|
)
|
49
46
|
|
50
47
|
def _prepare_textures(self):
|
51
|
-
self._use_textures = self.extra_options.get("use_textures", True) and check_textures_availability(
|
52
|
-
self._processing.ctx
|
53
|
-
)
|
54
48
|
if self._use_textures:
|
55
49
|
d_sino_ref = self.d_sino_tex = allocate_texture(self._processing.ctx, self.sino_shape)
|
56
50
|
self._kernel_options["sourcemodule_options"].append("-DUSE_TEXTURES")
|
57
51
|
else:
|
52
|
+
self._d_sino = self._processing.allocate_array("_d_sino", self.sino_shape)
|
58
53
|
d_sino_ref = self._d_sino.data
|
59
|
-
self.kern_proj_args
|
54
|
+
self.kern_proj_args[2] = d_sino_ref
|
60
55
|
|
61
56
|
def _compile_kernels(self):
|
62
57
|
self._prepare_kernel_args()
|
@@ -77,7 +72,7 @@ class OpenCLBackprojector(BackprojectorBase):
|
|
77
72
|
else:
|
78
73
|
if id(self._d_sino) == id(sino):
|
79
74
|
return
|
80
|
-
return cl.enqueue_copy(self._processing.queue, self._d_sino.data, sino)
|
75
|
+
return cl.enqueue_copy(self._processing.queue, self._d_sino.data, sino.data)
|
81
76
|
|
82
77
|
def _set_kernel_slice_arg(self, d_slice):
|
83
78
|
self.kern_proj_args[1] = d_slice
|
nabu/reconstruction/filtering.py
CHANGED
@@ -2,8 +2,8 @@ from math import pi
|
|
2
2
|
import numpy as np
|
3
3
|
from scipy.fft import rfft, irfft
|
4
4
|
from silx.image.tomography import compute_fourier_filter, get_next_power
|
5
|
-
from ..
|
6
|
-
from ..utils import check_supported, get_num_threads
|
5
|
+
from ..processing.padding_base import PaddingBase
|
6
|
+
from ..utils import check_supported, get_num_threads
|
7
7
|
|
8
8
|
# # COMPAT.
|
9
9
|
# from .filtering_cuda import CudaSinoFilter
|
@@ -1,13 +1,14 @@
|
|
1
|
-
import pycuda.gpuarray as garray
|
2
1
|
import numpy as np
|
3
|
-
from ..cuda.kernel import CudaKernel
|
4
2
|
from ..cuda.processing import CudaProcessing
|
5
3
|
from ..utils import get_cuda_srcfile
|
6
|
-
from ..
|
4
|
+
from ..processing.padding_cuda import CudaPadding
|
5
|
+
from ..processing.fft_cuda import get_fft_class
|
7
6
|
from .filtering import SinoFilter
|
8
7
|
|
9
8
|
|
10
9
|
class CudaSinoFilter(SinoFilter):
|
10
|
+
default_extra_options = {**SinoFilter.default_extra_options, **{"fft_backend": "skcuda"}}
|
11
|
+
|
11
12
|
def __init__(
|
12
13
|
self,
|
13
14
|
sino_shape,
|
@@ -16,26 +17,23 @@ class CudaSinoFilter(SinoFilter):
|
|
16
17
|
extra_options=None,
|
17
18
|
cuda_options=None,
|
18
19
|
):
|
19
|
-
super().__init__(sino_shape, filter_name=filter_name, padding_mode=padding_mode, extra_options=extra_options)
|
20
20
|
self._cuda_options = cuda_options or {}
|
21
21
|
self.cuda = CudaProcessing(**self._cuda_options)
|
22
|
+
super().__init__(sino_shape, filter_name=filter_name, padding_mode=padding_mode, extra_options=extra_options)
|
22
23
|
self._init_kernels()
|
23
24
|
|
24
25
|
def _init_fft(self):
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
#
|
29
|
-
self.fft = CUFFT(
|
26
|
+
fft_cls = get_fft_class(self.extra_options["fft_backend"])
|
27
|
+
self.fft = fft_cls(
|
30
28
|
self.sino_padded_shape,
|
31
29
|
dtype=np.float32,
|
32
30
|
axes=(-1,),
|
33
31
|
)
|
34
32
|
|
35
33
|
def _allocate_memory(self):
|
36
|
-
self.d_filter_f =
|
37
|
-
self.d_sino_padded = self.fft.
|
38
|
-
self.d_sino_f = self.fft.
|
34
|
+
self.d_filter_f = self.cuda.allocate_array("d_filter_f", (self.sino_f_shape[-1],), dtype=np.complex64)
|
35
|
+
self.d_sino_padded = self.cuda.allocate_array("d_sino_padded", self.fft.shape)
|
36
|
+
self.d_sino_f = self.cuda.allocate_array("d_sino_f", self.fft.shape_out, dtype=np.complex64)
|
39
37
|
|
40
38
|
def set_filter(self, h_filt, normalize=True):
|
41
39
|
super().set_filter(h_filt, normalize=normalize)
|
@@ -50,7 +48,7 @@ class CudaSinoFilter(SinoFilter):
|
|
50
48
|
else:
|
51
49
|
kernel_name = "inplace_complex_mul_3Dby1D"
|
52
50
|
kernel_sig = "PPiii"
|
53
|
-
self.mult_kernel =
|
51
|
+
self.mult_kernel = self.cuda.kernel(kernel_name, filename=fname, signature=kernel_sig)
|
54
52
|
self.kern_args = (self.d_sino_f, self.d_filter_f)
|
55
53
|
self.kern_args += self.d_sino_f.shape[::-1]
|
56
54
|
# padding
|
@@ -76,7 +74,8 @@ class CudaSinoFilter(SinoFilter):
|
|
76
74
|
in self.d_sino_padded.
|
77
75
|
"""
|
78
76
|
self._check_array(sino)
|
79
|
-
|
77
|
+
if not (isinstance(sino, self.cuda.array_class)):
|
78
|
+
sino = self.cuda.set_array("sino", sino)
|
80
79
|
|
81
80
|
# Padding
|
82
81
|
self.padding_kernel(sino, output=self.d_sino_padded)
|
@@ -1,13 +1,12 @@
|
|
1
1
|
import numpy as np
|
2
2
|
from ..utils import get_opencl_srcfile
|
3
|
-
from ..opencl.kernel import OpenCLKernel
|
4
3
|
from ..opencl.processing import OpenCLProcessing
|
5
|
-
from ..
|
4
|
+
from ..processing.padding_opencl import OpenCLPadding
|
6
5
|
from ..opencl.memcpy import OpenCLMemcpy2D
|
7
6
|
from .filtering import SinoFilter
|
8
7
|
|
9
8
|
try:
|
10
|
-
from pyvkfft.opencl import VkFFTApp as clfft
|
9
|
+
from pyvkfft.opencl import VkFFTApp as clfft # pylint: disable=E0401
|
11
10
|
|
12
11
|
__has_vkfft__ = True
|
13
12
|
except:
|
@@ -50,7 +49,7 @@ class OpenCLSinoFilter(SinoFilter):
|
|
50
49
|
kernel_name = "inplace_complex_mul_2Dby1D"
|
51
50
|
else:
|
52
51
|
kernel_name = "inplace_complex_mul_3Dby1D"
|
53
|
-
self.mult_kernel =
|
52
|
+
self.mult_kernel = self.opencl.kernel(kernel_name, filename=fname)
|
54
53
|
# padding
|
55
54
|
self.padding_kernel = OpenCLPadding(
|
56
55
|
self.sino_shape,
|
@@ -100,6 +100,8 @@ class Projector:
|
|
100
100
|
self._d_strideJoseph = garray.zeros((2, self._dimrecy), np.int32)
|
101
101
|
self._d_strideLine = garray.zeros((2, self._dimrecy), np.int32)
|
102
102
|
self.d_axis_corrections = garray.zeros((self.nprojs,), np.float32)
|
103
|
+
if self.extra_options.get("axis_corrections", None) is not None:
|
104
|
+
self.d_axis_corrections.set(self.extra_options["axis_corrections"])
|
103
105
|
|
104
106
|
# Textures
|
105
107
|
self.d_image_cua = cuda.np_to_array(np.zeros((self.shape[0] + 2, self.shape[1] + 2), "f"), "C")
|
nabu/reconstruction/rings.py
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
import numpy as np
|
2
|
+
from scipy.fft import rfft, irfft
|
3
|
+
from silx.image.tomography import get_next_power
|
2
4
|
from ..thirdparty.pore3d_deringer_munch import munchetal_filter
|
3
|
-
from ..utils import get_2D_3D_shape
|
5
|
+
from ..utils import get_2D_3D_shape, get_num_threads, check_supported
|
6
|
+
from ..misc.fourier_filters import get_bandpass_filter
|
7
|
+
|
8
|
+
try:
|
9
|
+
from algotom.prep.removal import remove_all_stripe
|
10
|
+
|
11
|
+
__has_algotom__ = True
|
12
|
+
except ImportError:
|
13
|
+
__has_algotom__ = False
|
4
14
|
|
5
15
|
|
6
16
|
class MunchDeringer:
|
@@ -89,3 +99,150 @@ class MunchDeringer:
|
|
89
99
|
for i in range(n_sinos):
|
90
100
|
self._destripe_2D(sinos[i], output[i])
|
91
101
|
return output
|
102
|
+
|
103
|
+
|
104
|
+
class VoDeringer:
|
105
|
+
"""
|
106
|
+
An interface to Nghia Vo's "remove_all_stripe".
|
107
|
+
Needs algotom to run.
|
108
|
+
"""
|
109
|
+
|
110
|
+
def __init__(self, sinos_shape, **remove_all_stripe_options):
|
111
|
+
self._check_requirement()
|
112
|
+
self._get_shapes(sinos_shape)
|
113
|
+
self._remove_all_stripe_kwargs = remove_all_stripe_options
|
114
|
+
|
115
|
+
def _check_requirement(self):
|
116
|
+
if not __has_algotom__:
|
117
|
+
raise ImportError("Need algotom")
|
118
|
+
|
119
|
+
def _get_shapes(self, sinos_shape):
|
120
|
+
n_z, n_a, n_x = get_2D_3D_shape(sinos_shape)
|
121
|
+
self.sinos_shape = n_z, n_a, n_x
|
122
|
+
self.n_angles = n_a
|
123
|
+
self.n_z = n_z
|
124
|
+
self.n_x = n_x
|
125
|
+
|
126
|
+
def remove_rings_sinogram(self, sino, output=None):
|
127
|
+
new_sino = remove_all_stripe(sino, **self._remove_all_stripe_kwargs) # out-of-place
|
128
|
+
if output is not None:
|
129
|
+
output[:] = new_sino[:]
|
130
|
+
return output
|
131
|
+
return new_sino
|
132
|
+
|
133
|
+
def remove_rings_sinograms(self, sinos, output=None):
|
134
|
+
if output is None:
|
135
|
+
output = sinos
|
136
|
+
for i in range(sinos.shape[0]):
|
137
|
+
output[i] = self.remove_rings_sinogram(sinos[i])
|
138
|
+
return output
|
139
|
+
|
140
|
+
def remove_rings_radios(self, radios):
|
141
|
+
sinos = np.moveaxis(radios, 1, 0) # (n_a, n_z, n_x) --> (n_z, n_a, n_x)
|
142
|
+
return self.remove_rings_sinograms(sinos)
|
143
|
+
|
144
|
+
remove_rings = remove_rings_sinograms
|
145
|
+
|
146
|
+
|
147
|
+
class SinoMeanDeringer:
|
148
|
+
supported_modes = ["subtract", "divide"]
|
149
|
+
|
150
|
+
def __init__(self, sinos_shape, mode="subtract", filter_cutoff=None, padding_mode="edge", fft_num_threads=None):
|
151
|
+
"""
|
152
|
+
Rings correction with mean subtraction/division.
|
153
|
+
The principle of this method is to subtract (or divide) the sinogram by its mean along a certain axis.
|
154
|
+
In short:
|
155
|
+
sinogram -= filt(sinogram.mean(axis=0))
|
156
|
+
where `filt` is some bandpass filter.
|
157
|
+
|
158
|
+
Parameters
|
159
|
+
----------
|
160
|
+
sinos_shape: tuple of int
|
161
|
+
Sinograms shape, in the form (n_angles, n_x) or (n_sinos, n_angles, n_x)
|
162
|
+
mode: str, optional
|
163
|
+
Operation to do on the sinogram, either "subtract" or "divide"
|
164
|
+
filter_cutoff: tuple, optional
|
165
|
+
Cut-off of the bandpass filter applied on the sinogram profiles.
|
166
|
+
Empty (default) means no filtering.
|
167
|
+
Possible values forms are:
|
168
|
+
- (sigma_low, sigma_high): two float values defining the standard deviation of
|
169
|
+
gaussian(sigma_low) * (1 - gaussian(sigma_high)).
|
170
|
+
High values of sigma mean stronger effect of associated filters.
|
171
|
+
- ((cutoff_low, transition_low), (cutoff_high, transition_high))
|
172
|
+
where "cutoff" is in normalized Nyquist frequency (0.5 is the maximum frequency),
|
173
|
+
and "transition" is the width of filter decay in fraction of the cutoff frequency
|
174
|
+
padding_mode: str, optional
|
175
|
+
Padding mode when filtering the sinogram profile.
|
176
|
+
Should be "constant" (i.e "zeros") for mathematical correctness,
|
177
|
+
but in practice this yields a Gibbs effect when replicating the sinogram, so "edges" is recommended.
|
178
|
+
fft_num_threads: int, optional
|
179
|
+
How many threads to use for computing the fast Fourier transform when filtering the sinogram profile.
|
180
|
+
Defaut is all the available threads.
|
181
|
+
"""
|
182
|
+
self._get_shapes(sinos_shape)
|
183
|
+
check_supported(mode, self.supported_modes, "operation mode")
|
184
|
+
self.mode = mode
|
185
|
+
self._init_filter(filter_cutoff, fft_num_threads, padding_mode)
|
186
|
+
|
187
|
+
def _get_shapes(self, sinos_shape):
|
188
|
+
n_z, n_a, n_x = get_2D_3D_shape(sinos_shape)
|
189
|
+
self.sinos_shape = n_z, n_a, n_x
|
190
|
+
self.n_angles = n_a
|
191
|
+
self.n_z = n_z
|
192
|
+
self.n_x = n_x
|
193
|
+
|
194
|
+
def _init_filter(self, filter_cutoff, fft_num_threads, padding_mode):
|
195
|
+
self.filter_cutoff = filter_cutoff
|
196
|
+
self._filter_f = None
|
197
|
+
if filter_cutoff is None:
|
198
|
+
return
|
199
|
+
self._filter_size = get_next_power(self.n_x * 2)
|
200
|
+
self._filter_f = get_bandpass_filter(
|
201
|
+
(1, self._filter_size),
|
202
|
+
cutoff_lowpass=filter_cutoff[0],
|
203
|
+
cutoff_highpass=filter_cutoff[1],
|
204
|
+
use_rfft=True,
|
205
|
+
data_type=np.float32,
|
206
|
+
).ravel()
|
207
|
+
self._fft_n_threads = get_num_threads(fft_num_threads)
|
208
|
+
# compat
|
209
|
+
if padding_mode == "edges":
|
210
|
+
padding_mode = "edge"
|
211
|
+
#
|
212
|
+
self.padding_mode = padding_mode
|
213
|
+
size_diff = self._filter_size - self.n_x
|
214
|
+
self._pad_left, self._pad_right = size_diff // 2, size_diff - size_diff // 2
|
215
|
+
|
216
|
+
def _apply_filter(self, sino_profile):
|
217
|
+
if self._filter_f is None:
|
218
|
+
return sino_profile
|
219
|
+
|
220
|
+
sino_profile = np.pad(sino_profile, (self._pad_left, self._pad_right), mode=self.padding_mode)
|
221
|
+
|
222
|
+
sino_f = rfft(sino_profile, workers=self._fft_n_threads)
|
223
|
+
sino_f *= self._filter_f
|
224
|
+
|
225
|
+
return irfft(sino_f, workers=self._fft_n_threads)[self._pad_left : -self._pad_right] # ascontiguousarray ?
|
226
|
+
|
227
|
+
def remove_rings_sinogram(self, sino, output=None):
|
228
|
+
#
|
229
|
+
if output is not None:
|
230
|
+
raise NotImplementedError
|
231
|
+
#
|
232
|
+
sino_profile = sino.mean(axis=0)
|
233
|
+
sino_profile = self._apply_filter(sino_profile)
|
234
|
+
if self.mode == "subtract":
|
235
|
+
sino -= sino_profile
|
236
|
+
elif self.mode == "divide":
|
237
|
+
sino /= sino_profile
|
238
|
+
return sino
|
239
|
+
|
240
|
+
def remove_rings_sinograms(self, sinos, output=None):
|
241
|
+
#
|
242
|
+
if output is not None:
|
243
|
+
raise NotImplementedError
|
244
|
+
#
|
245
|
+
for i in range(sinos.shape[0]):
|
246
|
+
self.remove_rings_sinogram(sinos[i])
|
247
|
+
|
248
|
+
remove_rings = remove_rings_sinograms
|