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.
Files changed (183) hide show
  1. doc/conf.py +1 -1
  2. doc/doc_config.py +32 -0
  3. nabu/__init__.py +2 -1
  4. nabu/app/bootstrap_stitching.py +1 -1
  5. nabu/app/cli_configs.py +122 -2
  6. nabu/app/composite_cor.py +27 -2
  7. nabu/app/correct_rot.py +70 -0
  8. nabu/app/create_distortion_map_from_poly.py +42 -18
  9. nabu/app/diag_to_pix.py +358 -0
  10. nabu/app/diag_to_rot.py +449 -0
  11. nabu/app/generate_header.py +4 -3
  12. nabu/app/histogram.py +2 -2
  13. nabu/app/multicor.py +6 -1
  14. nabu/app/parse_reconstruction_log.py +151 -0
  15. nabu/app/prepare_weights_double.py +83 -22
  16. nabu/app/reconstruct.py +5 -1
  17. nabu/app/reconstruct_helical.py +7 -0
  18. nabu/app/reduce_dark_flat.py +6 -3
  19. nabu/app/rotate.py +4 -4
  20. nabu/app/stitching.py +16 -2
  21. nabu/app/tests/test_reduce_dark_flat.py +18 -2
  22. nabu/app/validator.py +4 -4
  23. nabu/cuda/convolution.py +8 -376
  24. nabu/cuda/fft.py +4 -0
  25. nabu/cuda/kernel.py +4 -4
  26. nabu/cuda/medfilt.py +5 -158
  27. nabu/cuda/padding.py +5 -71
  28. nabu/cuda/processing.py +23 -2
  29. nabu/cuda/src/ElementOp.cu +78 -0
  30. nabu/cuda/src/backproj.cu +28 -2
  31. nabu/cuda/src/fourier_wavelets.cu +2 -2
  32. nabu/cuda/src/normalization.cu +23 -0
  33. nabu/cuda/src/padding.cu +2 -2
  34. nabu/cuda/src/transpose.cu +16 -0
  35. nabu/cuda/utils.py +39 -0
  36. nabu/estimation/alignment.py +10 -1
  37. nabu/estimation/cor.py +808 -38
  38. nabu/estimation/cor_sino.py +7 -9
  39. nabu/estimation/tests/test_cor.py +85 -3
  40. nabu/io/reader.py +26 -18
  41. nabu/io/tests/test_cast_volume.py +3 -3
  42. nabu/io/tests/test_detector_distortion.py +3 -3
  43. nabu/io/tiffwriter_zmm.py +2 -2
  44. nabu/io/utils.py +14 -4
  45. nabu/io/writer.py +5 -3
  46. nabu/misc/fftshift.py +6 -0
  47. nabu/misc/histogram.py +5 -285
  48. nabu/misc/histogram_cuda.py +8 -104
  49. nabu/misc/kernel_base.py +3 -121
  50. nabu/misc/padding_base.py +5 -69
  51. nabu/misc/processing_base.py +3 -107
  52. nabu/misc/rotation.py +5 -62
  53. nabu/misc/rotation_cuda.py +5 -65
  54. nabu/misc/transpose.py +6 -0
  55. nabu/misc/unsharp.py +3 -78
  56. nabu/misc/unsharp_cuda.py +5 -52
  57. nabu/misc/unsharp_opencl.py +8 -85
  58. nabu/opencl/fft.py +6 -0
  59. nabu/opencl/kernel.py +21 -6
  60. nabu/opencl/padding.py +5 -72
  61. nabu/opencl/processing.py +27 -5
  62. nabu/opencl/src/backproj.cl +3 -3
  63. nabu/opencl/src/fftshift.cl +65 -12
  64. nabu/opencl/src/padding.cl +2 -2
  65. nabu/opencl/src/roll.cl +96 -0
  66. nabu/opencl/src/transpose.cl +16 -0
  67. nabu/pipeline/config_validators.py +63 -3
  68. nabu/pipeline/dataset_validator.py +2 -2
  69. nabu/pipeline/estimators.py +193 -35
  70. nabu/pipeline/fullfield/chunked.py +34 -17
  71. nabu/pipeline/fullfield/chunked_cuda.py +7 -5
  72. nabu/pipeline/fullfield/computations.py +48 -13
  73. nabu/pipeline/fullfield/nabu_config.py +13 -13
  74. nabu/pipeline/fullfield/processconfig.py +10 -5
  75. nabu/pipeline/fullfield/reconstruction.py +1 -2
  76. nabu/pipeline/helical/fbp.py +5 -0
  77. nabu/pipeline/helical/filtering.py +12 -9
  78. nabu/pipeline/helical/gridded_accumulator.py +179 -33
  79. nabu/pipeline/helical/helical_chunked_regridded.py +262 -151
  80. nabu/pipeline/helical/helical_chunked_regridded_cuda.py +4 -11
  81. nabu/pipeline/helical/helical_reconstruction.py +56 -18
  82. nabu/pipeline/helical/span_strategy.py +1 -1
  83. nabu/pipeline/helical/tests/test_accumulator.py +4 -0
  84. nabu/pipeline/params.py +23 -2
  85. nabu/pipeline/processconfig.py +3 -8
  86. nabu/pipeline/tests/test_chunk_reader.py +78 -0
  87. nabu/pipeline/tests/test_estimators.py +120 -2
  88. nabu/pipeline/utils.py +25 -0
  89. nabu/pipeline/writer.py +2 -0
  90. nabu/preproc/ccd_cuda.py +9 -7
  91. nabu/preproc/ctf.py +21 -26
  92. nabu/preproc/ctf_cuda.py +25 -25
  93. nabu/preproc/double_flatfield.py +14 -2
  94. nabu/preproc/double_flatfield_cuda.py +7 -11
  95. nabu/preproc/flatfield_cuda.py +23 -27
  96. nabu/preproc/phase.py +19 -24
  97. nabu/preproc/phase_cuda.py +21 -21
  98. nabu/preproc/shift_cuda.py +58 -28
  99. nabu/preproc/tests/test_ctf.py +5 -5
  100. nabu/preproc/tests/test_double_flatfield.py +2 -2
  101. nabu/preproc/tests/test_vshift.py +13 -2
  102. nabu/processing/__init__.py +0 -0
  103. nabu/processing/convolution_cuda.py +375 -0
  104. nabu/processing/fft_base.py +163 -0
  105. nabu/processing/fft_cuda.py +256 -0
  106. nabu/processing/fft_opencl.py +54 -0
  107. nabu/processing/fftshift.py +134 -0
  108. nabu/processing/histogram.py +286 -0
  109. nabu/processing/histogram_cuda.py +103 -0
  110. nabu/processing/kernel_base.py +126 -0
  111. nabu/processing/medfilt_cuda.py +159 -0
  112. nabu/processing/muladd.py +29 -0
  113. nabu/processing/muladd_cuda.py +68 -0
  114. nabu/processing/padding_base.py +71 -0
  115. nabu/processing/padding_cuda.py +75 -0
  116. nabu/processing/padding_opencl.py +77 -0
  117. nabu/processing/processing_base.py +123 -0
  118. nabu/processing/roll_opencl.py +64 -0
  119. nabu/processing/rotation.py +63 -0
  120. nabu/processing/rotation_cuda.py +66 -0
  121. nabu/processing/tests/__init__.py +0 -0
  122. nabu/processing/tests/test_fft.py +268 -0
  123. nabu/processing/tests/test_fftshift.py +71 -0
  124. nabu/{misc → processing}/tests/test_histogram.py +2 -4
  125. nabu/{cuda → processing}/tests/test_medfilt.py +1 -1
  126. nabu/processing/tests/test_muladd.py +54 -0
  127. nabu/{cuda → processing}/tests/test_padding.py +119 -75
  128. nabu/processing/tests/test_roll.py +63 -0
  129. nabu/{misc → processing}/tests/test_rotation.py +3 -2
  130. nabu/processing/tests/test_transpose.py +72 -0
  131. nabu/{misc → processing}/tests/test_unsharp.py +41 -8
  132. nabu/processing/transpose.py +126 -0
  133. nabu/processing/unsharp.py +79 -0
  134. nabu/processing/unsharp_cuda.py +53 -0
  135. nabu/processing/unsharp_opencl.py +75 -0
  136. nabu/reconstruction/fbp.py +34 -10
  137. nabu/reconstruction/fbp_base.py +35 -16
  138. nabu/reconstruction/fbp_opencl.py +7 -12
  139. nabu/reconstruction/filtering.py +2 -2
  140. nabu/reconstruction/filtering_cuda.py +13 -14
  141. nabu/reconstruction/filtering_opencl.py +3 -4
  142. nabu/reconstruction/projection.py +2 -0
  143. nabu/reconstruction/rings.py +158 -1
  144. nabu/reconstruction/rings_cuda.py +218 -58
  145. nabu/reconstruction/sinogram_cuda.py +16 -12
  146. nabu/reconstruction/tests/test_deringer.py +116 -14
  147. nabu/reconstruction/tests/test_fbp.py +22 -31
  148. nabu/reconstruction/tests/test_filtering.py +11 -2
  149. nabu/resources/dataset_analyzer.py +89 -26
  150. nabu/resources/nxflatfield.py +2 -2
  151. nabu/resources/tests/test_nxflatfield.py +1 -1
  152. nabu/resources/utils.py +9 -2
  153. nabu/stitching/alignment.py +184 -0
  154. nabu/stitching/config.py +241 -39
  155. nabu/stitching/definitions.py +6 -0
  156. nabu/stitching/frame_composition.py +4 -2
  157. nabu/stitching/overlap.py +99 -3
  158. nabu/stitching/sample_normalization.py +60 -0
  159. nabu/stitching/slurm_utils.py +10 -10
  160. nabu/stitching/tests/test_alignment.py +99 -0
  161. nabu/stitching/tests/test_config.py +16 -1
  162. nabu/stitching/tests/test_overlap.py +68 -2
  163. nabu/stitching/tests/test_sample_normalization.py +49 -0
  164. nabu/stitching/tests/test_slurm_utils.py +5 -5
  165. nabu/stitching/tests/test_utils.py +3 -33
  166. nabu/stitching/tests/test_z_stitching.py +391 -22
  167. nabu/stitching/utils.py +144 -202
  168. nabu/stitching/z_stitching.py +309 -126
  169. nabu/testutils.py +18 -0
  170. nabu/thirdparty/tomocupy_remove_stripe.py +586 -0
  171. nabu/utils.py +32 -6
  172. {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/LICENSE +1 -1
  173. {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/METADATA +5 -5
  174. nabu-2024.1.0rc3.dist-info/RECORD +296 -0
  175. {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/WHEEL +1 -1
  176. {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/entry_points.txt +5 -1
  177. nabu/conftest.py +0 -14
  178. nabu/opencl/fftshift.py +0 -92
  179. nabu/opencl/tests/test_fftshift.py +0 -55
  180. nabu/opencl/tests/test_padding.py +0 -84
  181. nabu-2023.2.1.dist-info/RECORD +0 -252
  182. /nabu/cuda/src/{fftshift.cu → dfi_fftshift.cu} +0 -0
  183. {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  import numpy as np
2
2
  from ..utils import updiv, nextpow2, convert_index, deprecation_warning
3
- from ..misc.processing_base import ProcessingBase
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": round(self.rot_center - (self.n_x - 1) / 2.0),
167
- "y": round(self.rot_center - (self.n_y - 1) / 2.0),
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
- self.kern_proj_args[3] = rot_center
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": round(self.rot_center - (self.n_x - 1) / 2.0),
269
- "y": round(self.rot_center - (self.n_y - 1) / 2.0),
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[6] = self.offsets["x"]
272
- self.kern_proj_args[7] = self.offsets["y"]
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.int32(self.offsets["x"]),
315
- np.int32(self.offsets["y"]),
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
- self.sino_filter(sino, output=self._d_sino)
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
- self._d_sino[:] = sino[:] # we might avoid a memcopy here
360
- return self.backproj(self._d_sino, output=output)
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=%f, halftomo=%s)" % (
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 reset_rot_center(self, rot_center):
20
- """
21
- Define a new center of rotation for the current backprojector.
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.insert(2, d_sino_ref)
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
@@ -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 ..misc.padding_base import PaddingBase
6
- from ..utils import check_supported, get_num_threads, deprecated_class
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 ..cuda.padding import CudaPadding
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
- # Import has to be done here, otherwise scikit-cuda creates a cuda/cublas context at import
26
- from silx.math.fft.cufft import CUFFT
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 = garray.zeros((self.sino_f_shape[-1],), np.complex64)
37
- self.d_sino_padded = self.fft.data_in
38
- self.d_sino_f = self.fft.data_out
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 = CudaKernel(kernel_name, filename=fname, signature=kernel_sig)
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
- d_sino = sino = self.cuda.set_array("sino", sino)
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 ..opencl.padding import OpenCLPadding
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 = OpenCLKernel(kernel_name, self.opencl.ctx, filename=fname)
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")
@@ -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