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
@@ -0,0 +1,268 @@
1
+ from itertools import permutations
2
+ import pytest
3
+ import numpy as np
4
+ from scipy.fft import fftn, ifftn, rfftn, irfftn
5
+ from nabu.testutils import generate_tests_scenarios, get_data, get_array_of_given_shape, __do_long_tests__
6
+ from nabu.cuda.utils import get_cuda_context, __has_pycuda__
7
+ from nabu.processing.fft_cuda import SKCUFFT, VKCUFFT, has_vkfft as has_cuda_vkfft
8
+ from nabu.opencl.utils import __has_pyopencl__, get_opencl_context
9
+ from nabu.processing.fft_opencl import VKCLFFT, has_vkfft as has_cl_vkfft
10
+ from nabu.processing.fft_base import is_fast_axes
11
+
12
+ try:
13
+ import skcuda
14
+
15
+ __has_skcuda__ = True
16
+ except ImportError:
17
+ __has_skcuda__ = False
18
+
19
+
20
+ scenarios = {
21
+ "shape": [(256,), (300,), (300, 301), (300, 302)],
22
+ "r2c": [True, False],
23
+ "precision": ["simple"],
24
+ "backend": ["cuda", "opencl"],
25
+ }
26
+
27
+ if __do_long_tests__:
28
+ scenarios["shape"].extend([(307,), (125, 126, 260)])
29
+ scenarios["precision"].append("double")
30
+
31
+ scenarios = generate_tests_scenarios(scenarios)
32
+
33
+
34
+ @pytest.fixture(scope="class")
35
+ def bootstrap(request):
36
+ cls = request.cls
37
+
38
+ cls.data = get_data("chelsea.npz")["data"]
39
+ cls.abs_tol = {
40
+ "simple": {
41
+ 1: 5e-3,
42
+ 2: 1.0e0,
43
+ 3: 5e2, # !
44
+ },
45
+ "double": {
46
+ 1: 1e-10,
47
+ 2: 1e-9,
48
+ 3: 1e-7,
49
+ },
50
+ }
51
+ if __has_pycuda__:
52
+ cls.cu_ctx = get_cuda_context(cleanup_at_exit=False)
53
+ if __has_pyopencl__:
54
+ cls.cl_ctx = get_opencl_context("all")
55
+ yield
56
+ if __has_pycuda__:
57
+ cls.cu_ctx.pop()
58
+
59
+
60
+ def _get_fft_cls(backend):
61
+ fft_cls = None
62
+ if backend == "cuda":
63
+ if not (has_cuda_vkfft() and __has_pycuda__):
64
+ pytest.skip("Need vkfft and pycuda to use VKCUFFT")
65
+ fft_cls = VKCUFFT
66
+ if backend == "opencl":
67
+ if not (has_cl_vkfft() and __has_pyopencl__):
68
+ pytest.skip("Need vkfft and pyopencl to use VKCLFFT")
69
+ fft_cls = VKCLFFT
70
+ return fft_cls
71
+
72
+
73
+ @pytest.mark.usefixtures("bootstrap")
74
+ class TestFFT:
75
+ def _get_data_array(self, config):
76
+ r2c = config["r2c"]
77
+ shape = config["shape"]
78
+ precision = config["precision"]
79
+ dtype = {
80
+ True: {"simple": np.float32, "double": np.float64},
81
+ False: {"simple": np.complex64, "double": np.complex128},
82
+ }[r2c][precision]
83
+ data = get_array_of_given_shape(self.data, shape, dtype)
84
+ return data
85
+
86
+ @staticmethod
87
+ def check_result(res, ref, config, tol, name=""):
88
+ err_max = np.max(np.abs(res - ref))
89
+ err_msg = "%s FFT(%s, r2c=%s): tol=%.2e, but max error = %.2e" % (
90
+ name,
91
+ str(config["shape"]),
92
+ str(config["r2c"]),
93
+ tol,
94
+ err_max,
95
+ )
96
+ assert np.allclose(res, ref, atol=tol), err_msg
97
+
98
+ def _do_fft(self, data, r2c, axes=None, return_fft_obj=False, backend_cls=None):
99
+ ctx = self.cu_ctx if backend_cls.backend == "cuda" else self.cl_ctx
100
+ fft = backend_cls(data.shape, data.dtype, r2c=r2c, axes=axes, ctx=ctx)
101
+ d_data = fft.processing.allocate_array("_data", data.shape, dtype=data.dtype)
102
+ d_data.set(data)
103
+ d_out = fft.fft(d_data)
104
+ res = d_out.get()
105
+ return (res, fft) if return_fft_obj else res
106
+
107
+ @staticmethod
108
+ def _do_reference_fft(data, r2c, axes=None):
109
+ ref_fft_func = rfftn if r2c else fftn
110
+ ref = ref_fft_func(data, axes=axes)
111
+ return ref
112
+
113
+ @staticmethod
114
+ def _do_reference_ifft(data, r2c, axes=None):
115
+ ref_ifft_func = irfftn if r2c else ifftn
116
+ ref = ref_ifft_func(data, axes=axes)
117
+ return ref
118
+
119
+ @pytest.mark.skipif(not (__has_skcuda__ and __has_pycuda__), reason="Need pycuda scikit-cuda for this test")
120
+ @pytest.mark.parametrize("config", scenarios)
121
+ def test_sckcuda(self, config):
122
+ r2c = config["r2c"]
123
+ shape = config["shape"]
124
+ precision = config["precision"]
125
+ ndim = len(shape)
126
+ if ndim == 3 and not (__do_long_tests__):
127
+ pytest.skip("3D FFTs are done only for long tests - use NABU_LONG_TESTS=1")
128
+
129
+ data = self._get_data_array(config)
130
+
131
+ res, cufft = self._do_fft(data, r2c, return_fft_obj=True, backend_cls=SKCUFFT)
132
+ ref = self._do_reference_fft(data, r2c)
133
+
134
+ tol = self.abs_tol[precision][ndim]
135
+ self.check_result(res, ref, config, tol, name="skcuda")
136
+
137
+ # Complex-to-complex can also be performed on real data (as in numpy.fft.fft(real_data))
138
+ if not (r2c):
139
+ res = self._do_fft(data, False, backend_cls=SKCUFFT)
140
+ ref = self._do_reference_fft(data, False)
141
+ self.check_result(res, ref, config, tol, name="skcuda")
142
+
143
+ # IFFT
144
+ res = cufft.ifft(cufft.output_fft).get()
145
+ self.check_result(res, data, config, tol, name="skcuda")
146
+ # Perhaps we should also check against numpy/scipy ifft,
147
+ # but it does not yield the good shape for R2C on odd-sized data
148
+
149
+ @pytest.mark.skipif(not (__has_skcuda__ and __has_pycuda__), reason="Need pycuda scikit-cuda for this test")
150
+ @pytest.mark.parametrize("config", scenarios)
151
+ def test_skcuda_batched(self, config):
152
+ shape = config["shape"]
153
+ if len(shape) == 1:
154
+ return
155
+ elif len(shape) == 3 and not (__do_long_tests__):
156
+ pytest.skip("3D FFTs are done only for long tests - use NABU_LONG_TESTS=1")
157
+ r2c = config["r2c"]
158
+ tol = self.abs_tol[config["precision"]][len(shape)]
159
+
160
+ data = self._get_data_array(config)
161
+
162
+ if data.ndim == 2:
163
+ axes_to_test = [(0,), (1,)]
164
+ elif data.ndim == 3:
165
+ # axes_to_test = [(1, 2), (2, 1), (2,)] # See fft.py: works for C2C but not R2C ?
166
+ axes_to_test = [(2,)]
167
+
168
+ for axes in axes_to_test:
169
+ res, cufft = self._do_fft(data, r2c, axes=axes, return_fft_obj=True, backend_cls=SKCUFFT)
170
+ ref = self._do_reference_fft(data, r2c, axes=axes)
171
+ self.check_result(res, ref, config, tol, name="skcuda batched axes=%s" % (str(axes)))
172
+ # IFFT
173
+ res = cufft.ifft(cufft.output_fft).get()
174
+ self.check_result(res, data, config, tol, name="skcuda")
175
+
176
+ @pytest.mark.parametrize("config", scenarios)
177
+ def test_vkfft(self, config):
178
+ backend = config["backend"]
179
+ fft_cls = _get_fft_cls(backend)
180
+
181
+ r2c = config["r2c"]
182
+ shape = config["shape"]
183
+ precision = config["precision"]
184
+ ndim = len(shape)
185
+ if ndim == 3 and not (__do_long_tests__):
186
+ pytest.skip("3D FFTs are done only for long tests - use NABU_LONG_TESTS=1")
187
+
188
+ if ndim >= 2 and r2c and shape[-1] & 1:
189
+ pytest.skip("R2C with odd-sized fast dimension is not supported in VKFFT")
190
+
191
+ data = self._get_data_array(config)
192
+
193
+ res, fft_obj = self._do_fft(data, r2c, return_fft_obj=True, backend_cls=fft_cls)
194
+ ref = self._do_reference_fft(data, r2c)
195
+
196
+ tol = self.abs_tol[precision][ndim]
197
+ self.check_result(res, ref, config, tol, name="vkfft_%s" % backend)
198
+
199
+ # Complex-to-complex can also be performed on real data (as in numpy.fft.fft(real_data))
200
+ if not (r2c):
201
+ res = self._do_fft(data, False, backend_cls=fft_cls)
202
+ ref = self._do_reference_fft(data, False)
203
+ self.check_result(res, ref, config, tol, name="vkfft_%s" % backend)
204
+
205
+ # IFFT
206
+ res = fft_obj.ifft(fft_obj.output_fft).get()
207
+ self.check_result(res, data, config, tol, name="vkfft_%s" % backend)
208
+
209
+ @pytest.mark.parametrize("config", scenarios)
210
+ def test_vkfft_batched(self, config):
211
+ backend = config["backend"]
212
+ fft_cls = _get_fft_cls(backend)
213
+ shape = config["shape"]
214
+ if len(shape) == 1:
215
+ return
216
+ elif len(shape) == 3 and not (__do_long_tests__):
217
+ pytest.skip("3D FFTs are done only for long tests - use NABU_LONG_TESTS=1")
218
+ r2c = config["r2c"]
219
+ tol = self.abs_tol[config["precision"]][len(shape)]
220
+
221
+ data = self._get_data_array(config)
222
+
223
+ if data.ndim >= 2 and r2c and shape[-1] & 1:
224
+ pytest.skip("R2C with odd-sized fast dimension is not supported in VKFFT")
225
+
226
+ # For R2C, only fastest axes are supported by vkfft
227
+ if data.ndim == 2:
228
+ axes_to_test = [(1,)]
229
+ elif data.ndim == 3:
230
+ axes_to_test = [
231
+ (1, 2),
232
+ (2,),
233
+ ]
234
+ for axes in axes_to_test:
235
+ res, cufft = self._do_fft(data, r2c, axes=axes, return_fft_obj=True, backend_cls=fft_cls)
236
+ ref = self._do_reference_fft(data, r2c, axes=axes)
237
+ self.check_result(res, ref, config, tol, name="vkfft_%s batched axes=%s" % (backend, str(axes)))
238
+ # IFFT
239
+ res = cufft.ifft(cufft.output_fft).get()
240
+ self.check_result(res, data, config, tol, name="vkfft_%s" % backend)
241
+
242
+ @pytest.mark.skipif(not (__do_long_tests__), reason="Use NABU_LONG_TESTS=1 for this test")
243
+ def test_fast_axes_utility_function(self):
244
+ axes_to_test = {
245
+ 2: {
246
+ (0, 1): True,
247
+ (1,): True,
248
+ (-1,): True,
249
+ (-2,): False,
250
+ (0,): False,
251
+ },
252
+ 3: {
253
+ (0, 1, 2): True,
254
+ (0, 1): False,
255
+ (1, 2): True,
256
+ (2, 1): True,
257
+ (-2, -1): True,
258
+ (2,): True,
259
+ (-1,): True,
260
+ },
261
+ }
262
+ for ndim, axes_ in axes_to_test.items():
263
+ for axes, is_fast in axes_.items():
264
+ possible_axes = [axes]
265
+ if len(axes) > 1:
266
+ possible_axes = list(permutations(axes, len(axes)))
267
+ for ax in possible_axes:
268
+ assert is_fast_axes(ndim, ax) is is_fast
@@ -0,0 +1,71 @@
1
+ import numpy as np
2
+ import pytest
3
+ from nabu.cuda.utils import get_cuda_context, __has_pycuda__
4
+ from nabu.opencl.utils import __has_pyopencl__, get_opencl_context
5
+ from nabu.testutils import get_data, generate_tests_scenarios, __do_long_tests__
6
+
7
+ if __has_pyopencl__:
8
+ from nabu.processing.fftshift import OpenCLFFTshift
9
+
10
+ configs = {
11
+ "shape": [(300, 451), (300, 300), (255, 300)],
12
+ "axes": [(1,)],
13
+ "dtype_in_out": [(np.float32, np.complex64), (np.complex64, np.float32)],
14
+ "inplace": [True, False],
15
+ }
16
+
17
+ scenarios = generate_tests_scenarios(configs)
18
+
19
+
20
+ @pytest.fixture(scope="class")
21
+ def bootstrap(request):
22
+ cls = request.cls
23
+ cls.data = get_data("chelsea.npz")["data"]
24
+ cls.tol = 1e-7
25
+ if __has_pycuda__:
26
+ cls.cu_ctx = get_cuda_context(cleanup_at_exit=False)
27
+ if __has_pyopencl__:
28
+ cls.cl_ctx = get_opencl_context(device_type="all")
29
+ yield
30
+ if __has_pycuda__:
31
+ cls.cu_ctx.pop()
32
+
33
+
34
+ @pytest.mark.usefixtures("bootstrap")
35
+ class TestFFTshift:
36
+ def _do_test_fftshift(self, config, fftshift_cls):
37
+ shape = config["shape"]
38
+ dtype = config["dtype_in_out"][0]
39
+ dst_dtype = config["dtype_in_out"][1]
40
+ axes = config["axes"]
41
+ inplace = config["inplace"]
42
+ if inplace and shape[-1] & 1:
43
+ pytest.skip("Not Implemented")
44
+ data = np.ascontiguousarray(self.data[: shape[0], : shape[1]], dtype=dtype)
45
+
46
+ backend = fftshift_cls.backend
47
+ ctx = self.cu_ctx if backend == "cuda" else self.cl_ctx
48
+ backend_options = {"ctx": ctx}
49
+ if not (inplace):
50
+ fftshift = fftshift_cls(data.shape, dtype, dst_dtype=dst_dtype, axes=axes, **backend_options)
51
+ else:
52
+ fftshift = fftshift_cls(data.shape, dtype, axes=axes, **backend_options)
53
+
54
+ d_data = fftshift.processing.allocate_array("data", shape, dtype)
55
+ d_data.set(data)
56
+
57
+ d_res = fftshift.fftshift(d_data)
58
+
59
+ assert (
60
+ np.max(np.abs(d_res.get() - np.fft.fftshift(data, axes=axes))) == 0
61
+ ), "something wrong with fftshift_%s(%s)" % (backend, str(config))
62
+
63
+ # @pytest.mark.skipif(not (__has_pycuda__), reason="Need pycuda for this test")
64
+ # @pytest.mark.parametrize("config", scenarios)
65
+ # def test_cuda_transpose(self, config):
66
+ # self._do_test_transpose(config, CudaTranspose)
67
+
68
+ @pytest.mark.skipif(not (__has_pyopencl__), reason="Need pyopencl for this test")
69
+ @pytest.mark.parametrize("config", scenarios)
70
+ def test_opencl_fftshift(self, config):
71
+ self._do_test_fftshift(config, OpenCLFFTshift)
@@ -1,13 +1,11 @@
1
- from os import path
2
- from tempfile import mkdtemp
3
1
  import pytest
4
2
  import numpy as np
5
3
  from nabu.testutils import get_data
6
- from nabu.misc.histogram import PartialHistogram
4
+ from nabu.processing.histogram import PartialHistogram
7
5
  from nabu.cuda.utils import __has_pycuda__, get_cuda_context
8
6
 
9
7
  if __has_pycuda__:
10
- from nabu.misc.histogram_cuda import CudaPartialHistogram
8
+ from nabu.processing.histogram_cuda import CudaPartialHistogram
11
9
  import pycuda.gpuarray as garray
12
10
 
13
11
 
@@ -5,7 +5,7 @@ from nabu.testutils import generate_tests_scenarios, get_data
5
5
  from nabu.cuda.utils import get_cuda_context, __has_pycuda__
6
6
 
7
7
  if __has_pycuda__:
8
- from nabu.cuda.medfilt import MedianFilter
8
+ from nabu.processing.medfilt_cuda import MedianFilter
9
9
  import pycuda.gpuarray as garray
10
10
 
11
11
 
@@ -0,0 +1,54 @@
1
+ import pytest
2
+ import numpy as np
3
+ from nabu.processing.muladd import MulAdd
4
+ from nabu.testutils import get_data
5
+ from nabu.cuda.utils import get_cuda_context, __has_pycuda__
6
+
7
+ if __has_pycuda__:
8
+ from nabu.processing.muladd_cuda import CudaMulAdd
9
+
10
+
11
+ @pytest.fixture(scope="class")
12
+ def bootstrap(request):
13
+ cls = request.cls
14
+ cls.data = get_data("chelsea.npz")["data"].astype("f") # (300, 451)
15
+ cls.tol = 1e-7
16
+ if __has_pycuda__:
17
+ cls.cu_ctx = get_cuda_context(cleanup_at_exit=False)
18
+ yield
19
+ if __has_pycuda__:
20
+ cls.cu_ctx.pop()
21
+
22
+
23
+ @pytest.mark.usefixtures("bootstrap")
24
+ class TestMulad:
25
+ def test_muladd(self):
26
+ dst = self.data.copy()
27
+ other = self.data.copy()
28
+ mul_add = MulAdd()
29
+
30
+ # Test with no subregion
31
+ mul_add(dst, other, 1, 2)
32
+ assert np.allclose(dst, self.data * 1 + other * 2)
33
+
34
+ # Test with x-y subregion
35
+ dst = self.data.copy()
36
+ mul_add(dst, other, 0.5, 1.7, (slice(10, 200), slice(15, 124)), (slice(100, 290), slice(200, 309)))
37
+ assert np.allclose(dst[10:200, 15:124], self.data[10:200, 15:124] * 0.5 + self.data[100:290, 200:309] * 1.7)
38
+
39
+ @pytest.mark.skipif(not (__has_pycuda__), reason="Need Cuda/pycuda for this test")
40
+ def test_cuda_muladd(self):
41
+ mul_add = CudaMulAdd(ctx=self.cu_ctx)
42
+ dst = mul_add.processing.to_device("dst", self.data)
43
+ other = mul_add.processing.to_device("other", (self.data / 2).astype("f"))
44
+
45
+ # Test with no subregion
46
+ mul_add(dst, other, 3, 5)
47
+ assert np.allclose(dst.get(), self.data * 3 + (self.data / 2) * 5)
48
+
49
+ # Test with x-y subregion
50
+ dst.set(self.data)
51
+ mul_add(dst, other, 0.5, 1.7, (slice(10, 200), slice(15, 124)), (slice(100, 290), slice(200, 309)))
52
+ assert np.allclose(
53
+ dst.get()[10:200, 15:124], self.data[10:200, 15:124] * 0.5 + (self.data / 2)[100:290, 200:309] * 1.7
54
+ )
@@ -1,13 +1,130 @@
1
1
  import numpy as np
2
2
  import pytest
3
3
  from nabu.cuda.utils import get_cuda_context, __has_pycuda__
4
+ from nabu.opencl.utils import __has_pyopencl__, get_opencl_context
5
+ from nabu.processing.padding_cuda import CudaPadding
6
+ from nabu.processing.padding_opencl import OpenCLPadding
4
7
  from nabu.utils import calc_padding_lengths, get_cuda_srcfile
8
+ from nabu.testutils import __do_long_tests__
5
9
  from nabu.testutils import get_data, generate_tests_scenarios
6
10
 
11
+ scenarios = {
12
+ "shape": [(511, 512), (512, 511)],
13
+ "pad_width": [((256, 255), (128, 127))],
14
+ "mode_cuda": CudaPadding.supported_modes[:2] if __has_pycuda__ else [],
15
+ "mode_opencl": OpenCLPadding.supported_modes[:2] if __has_pyopencl__ else [],
16
+ "constant_values": [0, ((1.0, 2.0), (3.0, 4.0))],
17
+ "output_is_none": [True, False],
18
+ "backend": ["cuda", "opencl"],
19
+ }
20
+
21
+
22
+ if __do_long_tests__:
23
+ scenarios["mode_cuda"] = CudaPadding.supported_modes if __has_pycuda__ else []
24
+ scenarios["mode_opencl"] = OpenCLPadding.supported_modes if __has_pyopencl__ else []
25
+ scenarios["pad_width"].extend([((0, 0), (6, 7))])
26
+
27
+ scenarios = generate_tests_scenarios(scenarios)
28
+
29
+
30
+ @pytest.fixture(scope="class")
31
+ def bootstrap(request):
32
+ cls = request.cls
33
+ cls.data = get_data("brain_phantom.npz")["data"]
34
+ cls.tol = 1e-7
35
+ if __has_pycuda__:
36
+ cls.cu_ctx = get_cuda_context(cleanup_at_exit=False)
37
+ if __has_pyopencl__:
38
+ cls.cl_ctx = get_opencl_context(device_type="all")
39
+ yield
40
+ if __has_pycuda__:
41
+ cls.cu_ctx.pop()
42
+
43
+
44
+ @pytest.mark.usefixtures("bootstrap")
45
+ class TestPadding:
46
+ @pytest.mark.parametrize("config", scenarios)
47
+ def test_padding(self, config):
48
+ backend = config["backend"]
49
+ shape = config["shape"]
50
+ padding_mode = config["mode_%s" % backend]
51
+ data = self.data[: shape[0], : shape[1]]
52
+ kwargs = {}
53
+ if padding_mode == "constant":
54
+ kwargs["constant_values"] = config["constant_values"]
55
+ ref = np.pad(data, config["pad_width"], mode=padding_mode, **kwargs)
56
+
57
+ PaddingCls = CudaPadding if backend == "cuda" else OpenCLPadding
58
+ if backend == "cuda":
59
+ backend_options = {"cuda_options": {"ctx": self.cu_ctx}}
60
+ else:
61
+ backend_options = {"opencl_options": {"ctx": self.cl_ctx}}
62
+
63
+ padding = PaddingCls(
64
+ config["shape"],
65
+ config["pad_width"],
66
+ mode=padding_mode,
67
+ constant_values=config["constant_values"],
68
+ **backend_options,
69
+ )
70
+ if config["output_is_none"]:
71
+ output = None
72
+ else:
73
+ output = padding.processing.allocate_array("output", ref.shape, dtype="f")
74
+
75
+ d_img = padding.processing.allocate_array("d_img", data.shape, dtype="f")
76
+ d_img.set(np.ascontiguousarray(data, dtype="f"))
77
+ res = padding.pad(d_img, output=output)
78
+
79
+ err_max = np.max(np.abs(res.get() - ref))
80
+ assert err_max < self.tol, str("Something wrong with padding for configuration %s" % (str(config)))
81
+
82
+ @pytest.mark.skipif(not (__has_pycuda__) and not (__has_pyopencl__), reason="need pycuda or pyopencl")
83
+ def test_custom_coordinate_transform(self):
84
+ data = self.data
85
+ R, C = np.indices(data.shape, dtype=np.int32)
86
+
87
+ pad_width = ((256, 255), (254, 251))
88
+ mode = "reflect"
89
+
90
+ coords_R = np.pad(R, pad_width[0], mode=mode)[:, 0]
91
+ coords_C = np.pad(C, pad_width[1], mode=mode)[0, :]
92
+
93
+ # Further transform of coordinates - here FFT layout
94
+ coords_R = np.roll(coords_R, -pad_width[0][0])
95
+ coords_C = np.roll(coords_C, -pad_width[1][0])
96
+
97
+ padding_classes_to_test = []
98
+ if __has_pycuda__:
99
+ padding_classes_to_test.append(CudaPadding)
100
+ if __has_pyopencl__:
101
+ padding_classes_to_test.append(OpenCLPadding)
102
+
103
+ for padding_cls in padding_classes_to_test:
104
+ ctx = self.cl_ctx if padding_cls.backend == "opencl" else self.cu_ctx
105
+ padding = padding_cls(data.shape, (coords_R, coords_C), mode=mode, ctx=ctx)
106
+
107
+ d_img = padding.processing.allocate_array("d_img", data.shape, dtype="f")
108
+ d_img.set(data)
109
+ d_out = padding.processing.allocate_array("d_out", padding.padded_shape, dtype="f")
110
+
111
+ res = padding.pad(d_img, output=d_out)
112
+
113
+ ref = np.roll(np.pad(data, pad_width, mode=mode), (-pad_width[0][0], -pad_width[1][0]), axis=(0, 1))
114
+
115
+ err_max = np.max(np.abs(d_out.get() - ref))
116
+ assert err_max < self.tol, "Something wrong with custom padding"
117
+
118
+
119
+ #
120
+ # The following is testing a previous version of padding kernels
121
+ # They use specific code (instead of a generic coordinate transform)
122
+ #
123
+
7
124
  if __has_pycuda__:
8
- import pycuda.gpuarray as garray
9
125
  from nabu.cuda.kernel import CudaKernel
10
- from nabu.cuda.padding import CudaPadding
126
+ import pycuda.gpuarray as garray
127
+
11
128
 
12
129
  scenarios_legacy = [
13
130
  {
@@ -128,76 +245,3 @@ class TestPaddingLegacy:
128
245
  # Compare
129
246
  errmax = np.max(np.abs(self.d_data_padded.get() - data_padded_ref))
130
247
  assert errmax < self.tol, "Max error is too high"
131
-
132
-
133
- scenarios = generate_tests_scenarios(
134
- {
135
- "shape": [(511, 512), (512, 511)],
136
- "pad_width": [((256, 255), (128, 127)), ((0, 0), (6, 7))],
137
- "mode": CudaPadding.supported_modes if __has_pycuda__ else [],
138
- "constant_values": [0, ((1.0, 2.0), (3.0, 4.0))],
139
- "output_is_none": [True, False],
140
- }
141
- )
142
-
143
-
144
- @pytest.fixture(scope="class")
145
- def bootstrap(request):
146
- cls = request.cls
147
- cls.data = get_data("brain_phantom.npz")["data"]
148
- cls.tol = 1e-7
149
- cls.ctx = get_cuda_context(cleanup_at_exit=False)
150
- yield
151
- cls.ctx.pop()
152
-
153
-
154
- @pytest.mark.skipif(not (__has_pycuda__), reason="Need Cuda and pycuda for this test")
155
- @pytest.mark.usefixtures("bootstrap")
156
- class TestCudaPadding:
157
- @pytest.mark.parametrize("config", scenarios)
158
- def test_padding(self, config):
159
- shape = config["shape"]
160
- data = self.data[: shape[0], : shape[1]]
161
- kwargs = {}
162
- if config["mode"] == "constant":
163
- kwargs["constant_values"] = config["constant_values"]
164
- ref = np.pad(data, config["pad_width"], mode=config["mode"], **kwargs)
165
- if config["output_is_none"]:
166
- output = None
167
- else:
168
- output = garray.zeros(ref.shape, "f")
169
- cuda_padding = CudaPadding(
170
- config["shape"],
171
- config["pad_width"],
172
- mode=config["mode"],
173
- constant_values=config["constant_values"],
174
- cuda_options={"ctx": self.ctx},
175
- )
176
- d_img = garray.to_gpu(np.ascontiguousarray(data, dtype="f"))
177
- res = cuda_padding.pad(d_img, output=output)
178
-
179
- err_max = np.max(np.abs(res.get() - ref))
180
- assert err_max < self.tol, str("Something wrong with padding for configuration %s" % (str(config)))
181
-
182
- def test_custom_coordinate_transform(self):
183
- data = self.data
184
- R, C = np.indices(data.shape, dtype=np.int32)
185
-
186
- pad_width = ((256, 255), (254, 251))
187
- mode = "reflect"
188
-
189
- coords_R = np.pad(R, pad_width, mode=mode)
190
- coords_C = np.pad(C, pad_width, mode=mode)
191
- # Further transform of coordinates - here FFT layout
192
- coords_R = np.roll(coords_R, (-pad_width[0][0], -pad_width[1][0]), axis=(0, 1))
193
- coords_C = np.roll(coords_C, (-pad_width[0][0], -pad_width[1][0]), axis=(0, 1))
194
-
195
- cuda_padding = CudaPadding(data.shape, (coords_R, coords_C), mode=mode, cuda_options={"ctx": self.ctx})
196
- d_img = garray.to_gpu(data)
197
- d_out = garray.zeros(cuda_padding.padded_shape, "f")
198
- res = cuda_padding.pad(d_img, output=d_out)
199
-
200
- ref = np.roll(np.pad(data, pad_width, mode=mode), (-pad_width[0][0], -pad_width[1][0]), axis=(0, 1))
201
-
202
- err_max = np.max(np.abs(d_out.get() - ref))
203
- assert err_max < self.tol, "Something wrong with custom padding"
@@ -0,0 +1,63 @@
1
+ import numpy as np
2
+ import pytest
3
+ from nabu.cuda.utils import get_cuda_context, __has_pycuda__
4
+ from nabu.opencl.utils import __has_pyopencl__, get_opencl_context
5
+ from nabu.testutils import get_data, generate_tests_scenarios, __do_long_tests__
6
+ from nabu.processing.roll_opencl import OpenCLRoll
7
+
8
+ configs_roll = {
9
+ "shape": [(300, 451), (300, 300), (255, 300)],
10
+ "offset_x": [0, 10, 155],
11
+ "dtype": [np.float32], # , np.complex64],
12
+ }
13
+
14
+
15
+ scenarios_roll = generate_tests_scenarios(configs_roll)
16
+
17
+
18
+ @pytest.fixture(scope="class")
19
+ def bootstrap_roll(request):
20
+ cls = request.cls
21
+ cls.data = get_data("chelsea.npz")["data"]
22
+ cls.tol = 1e-7
23
+ if __has_pycuda__:
24
+ cls.cu_ctx = get_cuda_context(cleanup_at_exit=False)
25
+ if __has_pyopencl__:
26
+ cls.cl_ctx = get_opencl_context(device_type="all")
27
+ yield
28
+ if __has_pycuda__:
29
+ cls.cu_ctx.pop()
30
+
31
+
32
+ @pytest.mark.usefixtures("bootstrap_roll")
33
+ class TestRoll:
34
+ @staticmethod
35
+ def _compute_ref(data, direction, offset):
36
+ ref = data.copy()
37
+ ref[:, offset:] = np.roll(data[:, offset:], direction, axis=1)
38
+ return ref
39
+
40
+ @pytest.mark.skipif(not (__has_pyopencl__), reason="Need pyopencl for this test")
41
+ @pytest.mark.parametrize("config", scenarios_roll)
42
+ def test_opencl_roll(self, config):
43
+ shape = config["shape"]
44
+ dtype = config["dtype"]
45
+ offset_x = config["offset_x"]
46
+ data = np.ascontiguousarray(self.data[: shape[0], : shape[1]], dtype=dtype)
47
+
48
+ ref_forward = self._compute_ref(data, 1, offset_x)
49
+ ref_backward = self._compute_ref(data, -1, offset_x)
50
+
51
+ roll_forward = OpenCLRoll(dtype, direction=1, offset=offset_x, ctx=self.cl_ctx)
52
+ d_data = roll_forward.processing.allocate_array("data", data.shape, dtype=dtype)
53
+ d_data.set(data)
54
+ roll_backward = OpenCLRoll(dtype, direction=-1, offset=offset_x, queue=roll_forward.processing.queue)
55
+
56
+ roll_forward(d_data)
57
+ # from spire.utils import ims
58
+ # ims([d_data.get(), ref_forward, d_data.get() - ref_forward])
59
+ assert np.allclose(d_data.get(), ref_forward), "roll_forward: something wrong with config=%s" % (str(config))
60
+
61
+ d_data.set(data)
62
+ roll_backward(d_data)
63
+ assert np.allclose(d_data.get(), ref_backward), "roll_backward: something wrong with config=%s" % (str(config))