nabu 2024.1.9__py3-none-any.whl → 2024.2.0__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.
- nabu/__init__.py +1 -1
- nabu/app/bootstrap.py +2 -3
- nabu/app/cast_volume.py +4 -2
- nabu/app/cli_configs.py +5 -0
- nabu/app/composite_cor.py +1 -1
- nabu/app/create_distortion_map_from_poly.py +5 -6
- nabu/app/diag_to_pix.py +7 -19
- nabu/app/diag_to_rot.py +14 -29
- nabu/app/double_flatfield.py +32 -44
- nabu/app/parse_reconstruction_log.py +3 -0
- nabu/app/reconstruct.py +53 -15
- nabu/app/reconstruct_helical.py +2 -2
- nabu/app/stitching.py +27 -13
- nabu/app/tests/test_reduce_dark_flat.py +4 -1
- nabu/cuda/kernel.py +11 -2
- nabu/cuda/processing.py +2 -2
- nabu/cuda/src/cone.cu +77 -0
- nabu/cuda/src/hierarchical_backproj.cu +271 -0
- nabu/cuda/utils.py +0 -6
- nabu/estimation/alignment.py +5 -19
- nabu/estimation/cor.py +173 -599
- nabu/estimation/cor_sino.py +356 -26
- nabu/estimation/focus.py +63 -11
- nabu/estimation/tests/test_cor.py +124 -58
- nabu/estimation/tests/test_focus.py +6 -6
- nabu/estimation/tilt.py +2 -1
- nabu/estimation/utils.py +5 -33
- nabu/io/__init__.py +1 -1
- nabu/io/cast_volume.py +1 -1
- nabu/io/reader.py +416 -21
- nabu/io/tests/test_readers.py +422 -0
- nabu/io/tests/test_writers.py +1 -102
- nabu/io/writer.py +4 -433
- nabu/opencl/kernel.py +14 -3
- nabu/opencl/processing.py +8 -0
- nabu/pipeline/config_validators.py +5 -2
- nabu/pipeline/datadump.py +12 -5
- nabu/pipeline/estimators.py +162 -188
- nabu/pipeline/fullfield/chunked.py +168 -92
- nabu/pipeline/fullfield/chunked_cuda.py +7 -3
- nabu/pipeline/fullfield/computations.py +2 -7
- nabu/pipeline/fullfield/dataset_validator.py +0 -4
- nabu/pipeline/fullfield/nabu_config.py +37 -13
- nabu/pipeline/fullfield/processconfig.py +22 -13
- nabu/pipeline/fullfield/reconstruction.py +13 -9
- nabu/pipeline/helical/helical_chunked_regridded.py +1 -1
- nabu/pipeline/helical/helical_chunked_regridded_cuda.py +1 -0
- nabu/pipeline/helical/helical_reconstruction.py +1 -1
- nabu/pipeline/params.py +21 -1
- nabu/pipeline/processconfig.py +1 -12
- nabu/pipeline/reader.py +146 -0
- nabu/pipeline/tests/test_estimators.py +44 -72
- nabu/pipeline/utils.py +4 -2
- nabu/pipeline/writer.py +10 -2
- nabu/preproc/ccd_cuda.py +1 -1
- nabu/preproc/ctf.py +14 -7
- nabu/preproc/ctf_cuda.py +2 -3
- nabu/preproc/double_flatfield.py +5 -12
- nabu/preproc/double_flatfield_cuda.py +2 -2
- nabu/preproc/flatfield.py +5 -1
- nabu/preproc/flatfield_cuda.py +5 -1
- nabu/preproc/phase.py +24 -73
- nabu/preproc/phase_cuda.py +5 -8
- nabu/preproc/tests/test_ctf.py +11 -7
- nabu/preproc/tests/test_flatfield.py +67 -122
- nabu/preproc/tests/test_paganin.py +54 -30
- nabu/processing/azim.py +206 -0
- nabu/processing/convolution_cuda.py +1 -1
- nabu/processing/fft_cuda.py +15 -17
- nabu/processing/histogram.py +2 -0
- nabu/processing/histogram_cuda.py +2 -1
- nabu/processing/kernel_base.py +3 -0
- nabu/processing/muladd_cuda.py +1 -0
- nabu/processing/padding_opencl.py +1 -1
- nabu/processing/roll_opencl.py +1 -0
- nabu/processing/rotation_cuda.py +2 -2
- nabu/processing/tests/test_fft.py +17 -10
- nabu/processing/unsharp_cuda.py +1 -1
- nabu/reconstruction/cone.py +104 -40
- nabu/reconstruction/fbp.py +3 -0
- nabu/reconstruction/fbp_base.py +7 -2
- nabu/reconstruction/filtering.py +20 -7
- nabu/reconstruction/filtering_cuda.py +7 -1
- nabu/reconstruction/hbp.py +424 -0
- nabu/reconstruction/mlem.py +99 -0
- nabu/reconstruction/reconstructor.py +2 -0
- nabu/reconstruction/rings_cuda.py +19 -19
- nabu/reconstruction/sinogram_cuda.py +1 -0
- nabu/reconstruction/sinogram_opencl.py +3 -1
- nabu/reconstruction/tests/test_cone.py +10 -5
- nabu/reconstruction/tests/test_deringer.py +7 -6
- nabu/reconstruction/tests/test_fbp.py +124 -10
- nabu/reconstruction/tests/test_filtering.py +13 -11
- nabu/reconstruction/tests/test_halftomo.py +30 -4
- nabu/reconstruction/tests/test_mlem.py +91 -0
- nabu/reconstruction/tests/test_reconstructor.py +8 -3
- nabu/resources/dataset_analyzer.py +142 -92
- nabu/resources/gpu.py +1 -0
- nabu/resources/nxflatfield.py +134 -125
- nabu/resources/templates/id16a_fluo.conf +42 -0
- nabu/resources/tests/test_extract.py +10 -0
- nabu/resources/tests/test_nxflatfield.py +2 -2
- nabu/stitching/alignment.py +80 -24
- nabu/stitching/config.py +105 -68
- nabu/stitching/definitions.py +1 -0
- nabu/stitching/frame_composition.py +68 -60
- nabu/stitching/overlap.py +91 -51
- nabu/stitching/single_axis_stitching.py +32 -0
- nabu/stitching/slurm_utils.py +6 -6
- nabu/stitching/stitcher/__init__.py +0 -0
- nabu/stitching/stitcher/base.py +124 -0
- nabu/stitching/stitcher/dumper/__init__.py +3 -0
- nabu/stitching/stitcher/dumper/base.py +94 -0
- nabu/stitching/stitcher/dumper/postprocessing.py +356 -0
- nabu/stitching/stitcher/dumper/preprocessing.py +60 -0
- nabu/stitching/stitcher/post_processing.py +555 -0
- nabu/stitching/stitcher/pre_processing.py +1068 -0
- nabu/stitching/stitcher/single_axis.py +484 -0
- nabu/stitching/stitcher/stitcher.py +0 -0
- nabu/stitching/stitcher/y_stitcher.py +13 -0
- nabu/stitching/stitcher/z_stitcher.py +45 -0
- nabu/stitching/stitcher_2D.py +278 -0
- nabu/stitching/tests/test_config.py +12 -37
- nabu/stitching/tests/test_frame_composition.py +33 -59
- nabu/stitching/tests/test_overlap.py +149 -7
- nabu/stitching/tests/test_utils.py +1 -1
- nabu/stitching/tests/test_y_preprocessing_stitching.py +132 -0
- nabu/stitching/tests/{test_z_stitching.py → test_z_postprocessing_stitching.py} +167 -561
- nabu/stitching/tests/test_z_preprocessing_stitching.py +431 -0
- nabu/stitching/utils/__init__.py +1 -0
- nabu/stitching/utils/post_processing.py +281 -0
- nabu/stitching/utils/tests/test_post-processing.py +21 -0
- nabu/stitching/{utils.py → utils/utils.py} +79 -52
- nabu/stitching/y_stitching.py +27 -0
- nabu/stitching/z_stitching.py +32 -2263
- nabu/testutils.py +1 -152
- nabu/thirdparty/tomocupy_remove_stripe.py +43 -9
- nabu/utils.py +158 -61
- {nabu-2024.1.9.dist-info → nabu-2024.2.0.dist-info}/METADATA +10 -3
- {nabu-2024.1.9.dist-info → nabu-2024.2.0.dist-info}/RECORD +144 -121
- nabu/io/tiffwriter_zmm.py +0 -99
- nabu/pipeline/fallback_utils.py +0 -149
- nabu/pipeline/helical/tests/test_accumulator.py +0 -158
- nabu/pipeline/helical/tests/test_pipeline_elements_full.py +0 -355
- nabu/pipeline/helical/tests/test_strategy.py +0 -61
- nabu/pipeline/helical/utils.py +0 -51
- nabu/pipeline/tests/test_chunk_reader.py +0 -74
- {nabu-2024.1.9.dist-info → nabu-2024.2.0.dist-info}/LICENSE +0 -0
- {nabu-2024.1.9.dist-info → nabu-2024.2.0.dist-info}/WHEEL +0 -0
- {nabu-2024.1.9.dist-info → nabu-2024.2.0.dist-info}/entry_points.txt +0 -0
- {nabu-2024.1.9.dist-info → nabu-2024.2.0.dist-info}/top_level.txt +0 -0
nabu/reconstruction/cone.py
CHANGED
@@ -1,5 +1,10 @@
|
|
1
|
+
from math import sqrt
|
1
2
|
import numpy as np
|
2
|
-
|
3
|
+
|
4
|
+
from ..cuda.kernel import CudaKernel
|
5
|
+
from ..cuda.processing import CudaProcessing
|
6
|
+
from ..reconstruction.filtering_cuda import CudaSinoFilter
|
7
|
+
from ..utils import get_cuda_srcfile
|
3
8
|
|
4
9
|
try:
|
5
10
|
import astra
|
@@ -7,8 +12,6 @@ try:
|
|
7
12
|
__have_astra__ = True
|
8
13
|
except ImportError:
|
9
14
|
__have_astra__ = False
|
10
|
-
from ..cuda.processing import CudaProcessing
|
11
|
-
from ..processing.padding_cuda import CudaPadding
|
12
15
|
|
13
16
|
|
14
17
|
class ConebeamReconstructor:
|
@@ -16,6 +19,16 @@ class ConebeamReconstructor:
|
|
16
19
|
A reconstructor for cone-beam geometry using the astra toolbox.
|
17
20
|
"""
|
18
21
|
|
22
|
+
default_extra_options = {
|
23
|
+
"axis_correction": None,
|
24
|
+
"clip_outer_circle": False,
|
25
|
+
"scale_factor": None,
|
26
|
+
"filter_cutoff": 1.0,
|
27
|
+
"outer_circle_value": 0.0,
|
28
|
+
# "use_astra_fdk": True,
|
29
|
+
"use_astra_fdk": False,
|
30
|
+
}
|
31
|
+
|
19
32
|
def __init__(
|
20
33
|
self,
|
21
34
|
sinos_shape,
|
@@ -25,12 +38,12 @@ class ConebeamReconstructor:
|
|
25
38
|
volume_shape=None,
|
26
39
|
rot_center=None,
|
27
40
|
relative_z_position=None,
|
28
|
-
axis_correction=None,
|
29
41
|
pixel_size=None,
|
30
|
-
scale_factor=None,
|
31
42
|
padding_mode="zeros",
|
43
|
+
filter_name=None,
|
32
44
|
slice_roi=None,
|
33
45
|
cuda_options=None,
|
46
|
+
extra_options=None,
|
34
47
|
):
|
35
48
|
"""
|
36
49
|
Initialize a cone beam reconstructor. This reconstructor works on slabs of data,
|
@@ -78,6 +91,10 @@ class ConebeamReconstructor:
|
|
78
91
|
This parameter must be in the form (start_x, end_x, start_y, end_y) with no negative values.
|
79
92
|
Note that the current implementation just crops the final reconstructed volume,
|
80
93
|
i.e there is no speed or memory benefit.
|
94
|
+
use_astra_fdk: bool
|
95
|
+
Whether to use the native Astra Toolbox FDK implementation.
|
96
|
+
If set to False, the cone-beam pre-weighting and projections padding/filtering is done by nabu.
|
97
|
+
Note that this parameter is automatically set to False if padding_mode != "zeros".
|
81
98
|
|
82
99
|
Notes
|
83
100
|
------
|
@@ -106,10 +123,10 @@ class ConebeamReconstructor:
|
|
106
123
|
Fast and flexible X-ray tomography using the ASTRA toolbox.
|
107
124
|
Optics Express. 24. 25129-25147. 10.1364/OE.24.025129.
|
108
125
|
"""
|
126
|
+
self._configure_extra_options(extra_options)
|
109
127
|
self._init_cuda(cuda_options)
|
110
|
-
self.scale_factor = scale_factor
|
111
128
|
self._set_sino_shape(sinos_shape)
|
112
|
-
self.
|
129
|
+
self._orig_prog_geom = None
|
113
130
|
self._init_geometry(
|
114
131
|
source_origin_dist,
|
115
132
|
origin_detector_dist,
|
@@ -118,13 +135,17 @@ class ConebeamReconstructor:
|
|
118
135
|
volume_shape,
|
119
136
|
rot_center,
|
120
137
|
relative_z_position,
|
121
|
-
axis_correction,
|
122
138
|
slice_roi,
|
123
139
|
)
|
140
|
+
self._init_fdk(padding_mode, filter_name)
|
124
141
|
self._alg_id = None
|
125
142
|
self._vol_id = None
|
126
143
|
self._proj_id = None
|
127
144
|
|
145
|
+
def _configure_extra_options(self, extra_options):
|
146
|
+
self.extra_options = self.default_extra_options.copy()
|
147
|
+
self.extra_options.update(extra_options or {})
|
148
|
+
|
128
149
|
def _init_cuda(self, cuda_options):
|
129
150
|
cuda_options = cuda_options or {}
|
130
151
|
self.cuda = CudaProcessing(**cuda_options)
|
@@ -135,19 +156,25 @@ class ConebeamReconstructor:
|
|
135
156
|
self.sinos_shape = sinos_shape
|
136
157
|
self.n_sinos, self.n_angles, self.prj_width = sinos_shape
|
137
158
|
|
138
|
-
def
|
139
|
-
self._pad_data = False
|
159
|
+
def _init_fdk(self, padding_mode, filter_name):
|
140
160
|
self.padding_mode = padding_mode
|
141
|
-
|
161
|
+
self._use_astra_fdk = bool(self.extra_options.get("use_astra_fdk", True))
|
162
|
+
self._use_astra_fdk &= padding_mode in ["zeros", "constant", None, "none"]
|
163
|
+
if self._use_astra_fdk:
|
142
164
|
return
|
143
|
-
self.
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
165
|
+
self.sino_filter = CudaSinoFilter(
|
166
|
+
self.sinos_shape[1:],
|
167
|
+
filter_name=filter_name,
|
168
|
+
padding_mode=self.padding_mode,
|
169
|
+
# TODO (?) configure FFT backend
|
170
|
+
extra_options={"cutoff": self.extra_options.get("filter_cutoff", 1.0)},
|
171
|
+
cuda_options={"ctx": self.cuda.ctx},
|
148
172
|
)
|
149
|
-
|
150
|
-
|
173
|
+
# In astra, FDK pre-weighting does the "n_a/(pi/2) multiplication"
|
174
|
+
# TODO not sure where this "magnification **2" factor comes from ?
|
175
|
+
mult_factor = self.n_angles / 3.141592 * 2 / (self.magnification**2)
|
176
|
+
self.sino_filter.set_filter(self.sino_filter.filter_f * mult_factor, normalize=False)
|
177
|
+
#
|
151
178
|
|
152
179
|
def _set_pixel_size(self, pixel_size):
|
153
180
|
if pixel_size is None:
|
@@ -173,12 +200,6 @@ class ConebeamReconstructor:
|
|
173
200
|
self._vol_geom_n_x = self.n_x - start_x * 2
|
174
201
|
self._vol_geom_n_y = self.n_y - start_y * 2
|
175
202
|
else:
|
176
|
-
# self._crop_data = True
|
177
|
-
# self._output_cropped_shape = (
|
178
|
-
# self.n_z,
|
179
|
-
# np.arange(self.n_y)[start_x:end_x].size,
|
180
|
-
# np.arange(self.n_x)[start_y:end_y].size,
|
181
|
-
# )
|
182
203
|
raise NotImplementedError(
|
183
204
|
"Cone-beam geometry supports only slice_roi centered around origin (got slice_roi=%s with n_x=%d, n_y=%d)"
|
184
205
|
% (str(slice_roi), self.n_x, self.n_y)
|
@@ -193,7 +214,6 @@ class ConebeamReconstructor:
|
|
193
214
|
volume_shape,
|
194
215
|
rot_center,
|
195
216
|
relative_z_position,
|
196
|
-
axis_correction,
|
197
217
|
slice_roi,
|
198
218
|
):
|
199
219
|
if angles is None:
|
@@ -215,7 +235,7 @@ class ConebeamReconstructor:
|
|
215
235
|
if rot_center is not None:
|
216
236
|
self._cor_shift = (self.sinos_shape[-1] - 1) / 2.0 - rot_center
|
217
237
|
self._set_pixel_size(pixel_size)
|
218
|
-
self._axis_corrections = axis_correction
|
238
|
+
self._axis_corrections = self.extra_options.get("axis_correction", None)
|
219
239
|
self._create_astra_proj_geometry(relative_z_position)
|
220
240
|
|
221
241
|
def _create_astra_proj_geometry(self, relative_z_position):
|
@@ -234,6 +254,8 @@ class ConebeamReconstructor:
|
|
234
254
|
)
|
235
255
|
self.relative_z_position = relative_z_position or 0.0
|
236
256
|
# This will turn the geometry of type "cone" into a geometry of type "cone_vec"
|
257
|
+
if self._orig_prog_geom is None:
|
258
|
+
self._orig_prog_geom = self.proj_geom
|
237
259
|
self.proj_geom = astra.geom_postalignment(self.proj_geom, (self._cor_shift, 0))
|
238
260
|
# (src, detector_center, u, v) = (srcX, srcY, srcZ, dX, dY, dZ, uX, uY, uZ, vX, vY, vZ)
|
239
261
|
vecs = self.proj_geom["Vectors"]
|
@@ -274,23 +296,29 @@ class ConebeamReconstructor:
|
|
274
296
|
# But it seems Astra modifies the input sinogram while doing FDK, so this might be not relevant
|
275
297
|
d_sinos = self.cuda.get_array("sinos")
|
276
298
|
|
277
|
-
if self._pad_data:
|
278
|
-
sinos_padded = self.cuda.allocate_array("sinos_padded", self._sinos_padded_shape, dtype="f")
|
279
|
-
for i in range(self.n_sinos):
|
280
|
-
self.padder.pad(self.cuda.sinos[i], output=sinos_padded[i])
|
281
|
-
d_sinos = sinos_padded
|
282
|
-
|
283
299
|
# self._proj_data_link = astra.data3d.GPULink(d_sinos.ptr, self.prj_width, self.n_angles, self.n_z, sinos.strides[-2])
|
284
300
|
self._proj_data_link = astra.data3d.GPULink(
|
285
301
|
d_sinos.ptr, self.prj_width, self.n_angles, self.n_sinos, d_sinos.strides[-2]
|
286
302
|
)
|
287
303
|
self._proj_id = astra.data3d.link("-sino", self.proj_geom, self._proj_data_link)
|
288
304
|
|
305
|
+
def _preprocess_data(self):
|
306
|
+
if self._use_astra_fdk:
|
307
|
+
return
|
308
|
+
d_sinos = self.cuda.sinos
|
309
|
+
fdk_preweighting(
|
310
|
+
d_sinos, self._orig_prog_geom, relative_z_position=self.relative_z_position, cor_shift=self._cor_shift
|
311
|
+
)
|
312
|
+
for i in range(d_sinos.shape[0]):
|
313
|
+
self.sino_filter.filter_sino(d_sinos[i], output=d_sinos[i])
|
314
|
+
|
289
315
|
def _update_reconstruction(self):
|
290
|
-
|
316
|
+
if self._use_astra_fdk:
|
317
|
+
cfg = astra.astra_dict("FDK_CUDA")
|
318
|
+
else:
|
319
|
+
cfg = astra.astra_dict("BP3D_CUDA")
|
291
320
|
cfg["ReconstructionDataId"] = self._vol_id
|
292
321
|
cfg["ProjectionDataId"] = self._proj_id
|
293
|
-
# TODO more options "eg. filter" ?
|
294
322
|
if self._alg_id is not None:
|
295
323
|
astra.algorithm.delete(self._alg_id)
|
296
324
|
self._alg_id = astra.algorithm.create(cfg)
|
@@ -308,17 +336,20 @@ class ConebeamReconstructor:
|
|
308
336
|
self._create_astra_proj_geometry(relative_z_position)
|
309
337
|
self._set_input(sinos)
|
310
338
|
self._set_output(output)
|
339
|
+
self._preprocess_data()
|
311
340
|
self._update_reconstruction()
|
312
341
|
astra.algorithm.run(self._alg_id)
|
342
|
+
#
|
343
|
+
# NB: Could also be done with
|
344
|
+
# from astra.experimental import direct_BP3D
|
345
|
+
# projector_id = astra.create_projector("cuda3d", self.proj_geom, self.vol_geom, options=None)
|
346
|
+
# direct_BP3D(projector_id, self._vol_link, self._proj_data_link)
|
347
|
+
#
|
313
348
|
result = self.cuda.get_array("output")
|
314
349
|
if output is None:
|
315
350
|
result = result.get()
|
316
|
-
if self.scale_factor is not None:
|
317
|
-
result *= np.float32(self.scale_factor) # in-place for pycuda
|
318
|
-
# if self._crop_data:
|
319
|
-
# self.cuda.allocate_array("output_cropped", self._output_cropped_shape, dtype=np.float32)
|
320
|
-
# for i in range(self.n_z):
|
321
|
-
# output
|
351
|
+
if self.extra_options.get("scale_factor", None) is not None:
|
352
|
+
result *= np.float32(self.extra_options["scale_factor"]) # in-place for pycuda
|
322
353
|
self.cuda.recover_arrays_references(["sinos", "output"])
|
323
354
|
return result
|
324
355
|
|
@@ -345,3 +376,36 @@ def roi_is_centered(shape, slice_):
|
|
345
376
|
Return True if "slice_" define a selection that is centered on the middle of the array.
|
346
377
|
"""
|
347
378
|
return all([selection_is_centered(shp, s.start, s.stop) for shp, s in zip(shape, slice_)])
|
379
|
+
|
380
|
+
|
381
|
+
def fdk_preweighting(d_sinos, proj_geom, relative_z_position=0.0, cor_shift=0.0):
|
382
|
+
|
383
|
+
preweight_kernel = CudaKernel(
|
384
|
+
"devFDK_preweight",
|
385
|
+
filename=get_cuda_srcfile("cone.cu"),
|
386
|
+
signature="Piiifffffiii",
|
387
|
+
)
|
388
|
+
|
389
|
+
# n_angles, n_z, n_x = d_sinos.shape
|
390
|
+
n_z, n_angles, n_x = d_sinos.shape
|
391
|
+
det_origin = sqrt(proj_geom["DistanceOriginDetector"] ** 2 + cor_shift**2)
|
392
|
+
|
393
|
+
block = (32, 16, 1)
|
394
|
+
grid = (((n_x + 32 - 1) // 32) * ((n_z + 32 - 1) // 32), (n_angles + 16 - 1) // 16, 1)
|
395
|
+
|
396
|
+
preweight_kernel(
|
397
|
+
d_sinos,
|
398
|
+
np.uint32(n_x), # unsigned int projPitch,
|
399
|
+
np.uint32(0), # unsigned int startAngle,
|
400
|
+
np.uint32(n_angles), # unsigned int endAngle,
|
401
|
+
np.float32(proj_geom["DistanceOriginSource"]), # float fSrcOrigin,
|
402
|
+
np.float32(det_origin), # float fDetOrigin,
|
403
|
+
np.float32(relative_z_position), # float fZShift,
|
404
|
+
np.float32(proj_geom["DetectorSpacingX"]), # float fDetUSize,
|
405
|
+
np.float32(proj_geom["DetectorSpacingY"]), # float fDetVSize,
|
406
|
+
np.int32(n_angles), # dims.iProjAngles;
|
407
|
+
np.int32(n_x), # dims.iProjU; // number of detectors in the U direction
|
408
|
+
np.int32(n_z), # dims.iProjV // number of detectors in the V direction
|
409
|
+
block=block,
|
410
|
+
grid=grid,
|
411
|
+
)
|
nabu/reconstruction/fbp.py
CHANGED
@@ -73,12 +73,15 @@ class CudaBackprojector(BackprojectorBase):
|
|
73
73
|
self._kernel_options["kernel_name"],
|
74
74
|
filename=self._kernel_options["file_name"],
|
75
75
|
options=self._kernel_options["sourcemodule_options"],
|
76
|
+
silent_compilation_warnings=True, # textures and Cuda 11
|
76
77
|
)
|
77
78
|
if self.halftomo and self.rot_center < self.dwidth:
|
78
79
|
self.sino_mult = CudaSinoMult(self.sino_shape, self.rot_center, ctx=self._processing.ctx)
|
79
80
|
self._prepare_textures() # has to be done after compilation for Cuda (to bind texture to built kernel)
|
80
81
|
|
81
82
|
def _transfer_to_texture(self, sino, do_checks=True):
|
83
|
+
if do_checks and not (sino.flags.c_contiguous):
|
84
|
+
raise ValueError("Expected C-Contiguous array")
|
82
85
|
if self._use_textures:
|
83
86
|
copy_array(self._d_sino_cua, sino, check=do_checks)
|
84
87
|
else:
|
nabu/reconstruction/fbp_base.py
CHANGED
@@ -21,6 +21,7 @@ class BackprojectorBase:
|
|
21
21
|
"clip_outer_circle": False,
|
22
22
|
"scale_factor": None,
|
23
23
|
"filter_cutoff": 1.0,
|
24
|
+
"outer_circle_value": 0.0,
|
24
25
|
}
|
25
26
|
|
26
27
|
kernel_filename = None
|
@@ -335,13 +336,17 @@ class BackprojectorBase:
|
|
335
336
|
self._output_is_ndarray = isinstance(output, np.ndarray)
|
336
337
|
if output is None or self._output_is_ndarray:
|
337
338
|
self._processing.allocate_array("_d_slice", self.slice_shape, dtype=np.float32)
|
338
|
-
|
339
|
-
|
339
|
+
output = self._processing._d_slice # pylint: disable=E1101
|
340
|
+
elif check:
|
340
341
|
assert output.dtype == np.float32
|
341
342
|
assert output.shape == self.slice_shape, "Expected output shape %s but got %s" % (
|
342
343
|
self.slice_shape,
|
343
344
|
output.shape,
|
344
345
|
)
|
346
|
+
if self.extra_options.get("clip_outer_circle", False):
|
347
|
+
out_circle_val = self.extra_options.get("outer_circle_value", 0)
|
348
|
+
if out_circle_val != 0:
|
349
|
+
output.fill(out_circle_val)
|
345
350
|
return output
|
346
351
|
|
347
352
|
def _set_kernel_slice_arg(self, d_slice):
|
nabu/reconstruction/filtering.py
CHANGED
@@ -162,9 +162,10 @@ class SinoFilter:
|
|
162
162
|
in self.d_sino_padded.
|
163
163
|
"""
|
164
164
|
self._check_array(sino)
|
165
|
-
sino_padded = np.pad(
|
166
|
-
|
167
|
-
) # pad with a FFT-friendly layout
|
165
|
+
# sino_padded = np.pad(
|
166
|
+
# sino, ((0, 0), (0, self.dwidth_padded - self.dwidth)), mode=self.padding_mode
|
167
|
+
# ) # pad with a FFT-friendly layout
|
168
|
+
sino_padded = np.pad(sino, ((0, 0), (self.pad_left, self.pad_right)), mode=self.padding_mode)
|
168
169
|
sino_padded_f = rfft(sino_padded, axis=1, workers=get_num_threads(self.extra_options["fft_threads"]))
|
169
170
|
sino_padded_f *= self.filter_f
|
170
171
|
sino_filtered = irfft(sino_padded_f, axis=1, workers=get_num_threads(self.extra_options["fft_threads"]))
|
@@ -173,9 +174,11 @@ class SinoFilter:
|
|
173
174
|
else:
|
174
175
|
res = output
|
175
176
|
if self.ndim == 2:
|
176
|
-
res[:] = sino_filtered[:, : self.dwidth] # pylint: disable=E1126 # ?!
|
177
|
+
# res[:] = sino_filtered[:, : self.dwidth] # pylint: disable=E1126 # ?!
|
178
|
+
res[:] = sino_filtered[:, self.pad_left : -self.pad_right] # pylint: disable=E1126 # ?!
|
177
179
|
else:
|
178
|
-
res[:] = sino_filtered[:, :, : self.dwidth] # pylint: disable=E1126 # ?!
|
180
|
+
# res[:] = sino_filtered[:, :, : self.dwidth] # pylint: disable=E1126 # ?!
|
181
|
+
res[:] = sino_filtered[:, :, self.pad_left : -self.pad_right] # pylint: disable=E1126 # ?!
|
179
182
|
return res
|
180
183
|
|
181
184
|
__call__ = filter_sino
|
@@ -209,12 +212,22 @@ def filter_sinogram(
|
|
209
212
|
frequency cutoff for filter
|
210
213
|
"""
|
211
214
|
n_angles, width = sinogram.shape
|
212
|
-
|
215
|
+
|
216
|
+
# Initially, padding was done this way
|
217
|
+
# sinogram_padded = np.pad(sinogram, ((0, 0), (0, padded_width - width)), mode=padding_mode, **padding_kwargs)
|
218
|
+
|
219
|
+
#
|
220
|
+
pad_left = (padded_width - width) // 2
|
221
|
+
pad_right = padded_width - width - pad_left
|
222
|
+
sinogram_padded = np.pad(sinogram, ((0, 0), (pad_left, pad_right)), mode=padding_mode, **padding_kwargs)
|
223
|
+
#
|
224
|
+
|
213
225
|
fourier_filter = compute_fourier_filter(padded_width, filter_name, cutoff=filter_cutoff)
|
214
226
|
if normalize:
|
215
227
|
fourier_filter *= np.pi / n_angles
|
216
228
|
fourier_filter = fourier_filter[: padded_width // 2 + 1] # R2C
|
217
229
|
sino_f = rfft(sinogram_padded, axis=1)
|
218
230
|
sino_f *= fourier_filter
|
219
|
-
sino_filtered = irfft(sino_f, axis=1)[:, :width] # pylint: disable=E1126 # ?!
|
231
|
+
# sino_filtered = irfft(sino_f, axis=1)[:, :width] # pylint: disable=E1126 # ?!
|
232
|
+
sino_filtered = irfft(sino_f, axis=1)[:, pad_left:-pad_right] # pylint: disable=E1126 # ?!
|
220
233
|
return sino_filtered
|
@@ -7,7 +7,7 @@ from .filtering import SinoFilter
|
|
7
7
|
|
8
8
|
|
9
9
|
class CudaSinoFilter(SinoFilter):
|
10
|
-
default_extra_options = {**SinoFilter.default_extra_options, **{"fft_backend": "
|
10
|
+
default_extra_options = {**SinoFilter.default_extra_options, **{"fft_backend": "vkfft"}}
|
11
11
|
|
12
12
|
def __init__(
|
13
13
|
self,
|
@@ -76,6 +76,12 @@ class CudaSinoFilter(SinoFilter):
|
|
76
76
|
self._check_array(sino)
|
77
77
|
if not (isinstance(sino, self.cuda.array_class)):
|
78
78
|
sino = self.cuda.set_array("sino", sino)
|
79
|
+
elif not (sino.flags.c_contiguous):
|
80
|
+
# Transfer the device array into another, c-contiguous, device array
|
81
|
+
# We can throw an error as well in this case, but often we so something like fbp(radios[:, i, :])
|
82
|
+
sino_tmp = self.cuda.allocate_array("sino_contig", sino.shape)
|
83
|
+
sino_tmp.set(sino)
|
84
|
+
sino = sino_tmp
|
79
85
|
|
80
86
|
# Padding
|
81
87
|
self.padding_kernel(sino, output=self.d_sino_padded)
|