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
@@ -1,16 +1,8 @@
|
|
1
|
-
from math import ceil
|
2
1
|
import numpy as np
|
3
|
-
from ...utils import deprecated
|
4
|
-
from ...preproc.flatfield_cuda import CudaFlatFieldDataUrls
|
5
|
-
from ...preproc.shift import VerticalShift
|
6
2
|
from ...preproc.shift_cuda import CudaVerticalShift
|
7
3
|
|
8
|
-
from ...preproc.phase_cuda import CudaPaganinPhaseRetrieval
|
9
4
|
from ...reconstruction.sinogram_cuda import CudaSinoBuilder, CudaSinoNormalization
|
10
|
-
from ...
|
11
|
-
from ...misc.unsharp_cuda import CudaUnsharpMask
|
12
|
-
from ...misc.rotation_cuda import CudaRotation
|
13
|
-
from ...misc.histogram_cuda import CudaPartialHistogram
|
5
|
+
from ...processing.histogram_cuda import CudaPartialHistogram
|
14
6
|
from .fbp import BackprojectorHelical
|
15
7
|
|
16
8
|
|
@@ -21,8 +13,7 @@ try:
|
|
21
13
|
except:
|
22
14
|
HierarchicalBackprojector = None
|
23
15
|
|
24
|
-
from ...cuda.utils import get_cuda_context, __has_pycuda__, __pycuda_error_msg__
|
25
|
-
from ..utils import pipeline_step
|
16
|
+
from ...cuda.utils import get_cuda_context, __has_pycuda__, __pycuda_error_msg__
|
26
17
|
from .helical_chunked_regridded import HelicalChunkedRegriddedPipeline
|
27
18
|
|
28
19
|
if __has_pycuda__:
|
@@ -53,6 +44,7 @@ class CudaHelicalChunkedRegriddedPipeline(HelicalChunkedRegriddedPipeline):
|
|
53
44
|
reading_granularity=10,
|
54
45
|
span_info=None,
|
55
46
|
num_weight_radios_per_app=1000,
|
47
|
+
diag_zpro_run=0,
|
56
48
|
):
|
57
49
|
self._init_cuda(cuda_options)
|
58
50
|
super().__init__(
|
@@ -63,6 +55,7 @@ class CudaHelicalChunkedRegriddedPipeline(HelicalChunkedRegriddedPipeline):
|
|
63
55
|
phase_margin=phase_margin,
|
64
56
|
reading_granularity=reading_granularity,
|
65
57
|
span_info=span_info,
|
58
|
+
diag_zpro_run=diag_zpro_run,
|
66
59
|
)
|
67
60
|
self._register_callbacks()
|
68
61
|
|
@@ -1,23 +1,24 @@
|
|
1
|
-
from os.path import join, isfile,
|
1
|
+
from os.path import join, isfile, dirname
|
2
2
|
from math import ceil
|
3
|
-
import gc
|
4
3
|
from time import time
|
5
|
-
from psutil import virtual_memory
|
6
|
-
from silx.io import get_data
|
7
|
-
from silx.io.url import DataUrl
|
8
4
|
import numpy as np
|
9
|
-
import math
|
10
5
|
import copy
|
11
6
|
from ...resources.logger import LoggerOrPrint
|
12
|
-
from ...io.writer import merge_hdf5_files
|
7
|
+
from ...io.writer import merge_hdf5_files
|
13
8
|
from ...cuda.utils import collect_cuda_gpus
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
|
10
|
+
try:
|
11
|
+
import nabuxx
|
12
|
+
|
13
|
+
SpanStrategy = nabuxx.span_strategy.SpanStrategy
|
14
|
+
except:
|
15
|
+
logger_tmp = LoggerOrPrint(None)
|
16
|
+
logger_tmp.info("Nabuxx not available. Loading python implementation for SpanStrategy")
|
17
|
+
from .span_strategy import SpanStrategy
|
17
18
|
|
18
19
|
from .helical_chunked_regridded_cuda import CudaHelicalChunkedRegriddedPipeline
|
19
20
|
|
20
|
-
from ..fullfield.reconstruction import collect_cuda_gpus,
|
21
|
+
from ..fullfield.reconstruction import collect_cuda_gpus, FullFieldReconstructor
|
21
22
|
|
22
23
|
avail_gpus = collect_cuda_gpus() or {}
|
23
24
|
|
@@ -62,12 +63,20 @@ class HelicalReconstructorRegridded:
|
|
62
63
|
- "max_chunk_size": None,
|
63
64
|
- "phase_margin": None,
|
64
65
|
- "dry_run": 0,
|
66
|
+
- "diag_zpro_run": 0,
|
65
67
|
"""
|
66
68
|
self.logger = LoggerOrPrint(logger)
|
67
69
|
self.process_config = process_config
|
68
70
|
self._set_extra_options(extra_options)
|
69
71
|
self._get_resources()
|
70
|
-
|
72
|
+
|
73
|
+
### intrication problem: this is used in fullfield's compute_margin to clamp the margin but not used by the present pipeline
|
74
|
+
### Set it to a big number that will never clamp
|
75
|
+
self.n_z = 10000000 # a big number
|
76
|
+
self.n_x = 10000000 # a big number
|
77
|
+
###
|
78
|
+
self._compute_margin()
|
79
|
+
# self._margin_v, self._margin_h = self._compute_phase_margin()
|
71
80
|
|
72
81
|
self._setup_span_info()
|
73
82
|
self._compute_max_chunk_size()
|
@@ -87,6 +96,7 @@ class HelicalReconstructorRegridded:
|
|
87
96
|
"max_chunk_size": None,
|
88
97
|
"phase_margin": None,
|
89
98
|
"dry_run": 0,
|
99
|
+
"diag_zpro_run": 0,
|
90
100
|
}
|
91
101
|
advanced_options.update(extra_options)
|
92
102
|
self.extra_options = advanced_options
|
@@ -95,8 +105,15 @@ class HelicalReconstructorRegridded:
|
|
95
105
|
self.use_phase_margin = self.extra_options["use_phase_margin"]
|
96
106
|
|
97
107
|
self.dry_run = self.extra_options["dry_run"]
|
108
|
+
self.diag_zpro_run = self.extra_options["diag_zpro_run"]
|
98
109
|
|
99
110
|
self._do_histograms = self.process_config.nabu_config["postproc"]["output_histogram"]
|
111
|
+
|
112
|
+
if self.diag_zpro_run:
|
113
|
+
self.process_config.processing_options.get("phase", None)
|
114
|
+
self._do_histograms = False
|
115
|
+
self.reading_granularity = 10
|
116
|
+
|
100
117
|
self._histogram_merged = False
|
101
118
|
self._span_info = None
|
102
119
|
|
@@ -145,13 +162,23 @@ class HelicalReconstructorRegridded:
|
|
145
162
|
break
|
146
163
|
elif z_fract_min != 0.0 or z_fract_max != 0.0:
|
147
164
|
z_start, z_max = (self._span_info.get_doable_span()).view_heights_minmax
|
148
|
-
|
149
|
-
|
165
|
+
|
166
|
+
# the meaming of z_min and z_max is: position in slices units from the
|
167
|
+
# first available slice and in the direction of the scan
|
168
|
+
self.z_min = int(round(z_start * (0 - z_fract_min) + z_max * z_fract_min))
|
169
|
+
self.z_max = int(round(z_start * (0 - z_fract_max) + z_max * z_fract_max)) + 1
|
170
|
+
|
171
|
+
def _compute_translations_margin(self):
|
172
|
+
return 0, 0
|
173
|
+
|
174
|
+
def _compute_cone_overlap(self):
|
175
|
+
return 0, 0
|
150
176
|
|
151
177
|
_get_resources = FullFieldReconstructor._get_resources
|
152
178
|
_get_memory = FullFieldReconstructor._get_memory
|
153
179
|
_get_gpu = FullFieldReconstructor._get_gpu
|
154
180
|
_compute_phase_margin = FullFieldReconstructor._compute_phase_margin
|
181
|
+
_compute_margin = FullFieldReconstructor._compute_margin
|
155
182
|
_compute_unsharp_margin = FullFieldReconstructor._compute_unsharp_margin
|
156
183
|
_print_tasks = FullFieldReconstructor._print_tasks
|
157
184
|
_instantiate_pipeline_if_necessary = FullFieldReconstructor._instantiate_pipeline_if_necessary
|
@@ -180,6 +207,14 @@ class HelicalReconstructorRegridded:
|
|
180
207
|
|
181
208
|
user_max_chunk_size = self.extra_options["max_chunk_size"]
|
182
209
|
|
210
|
+
if self.diag_zpro_run:
|
211
|
+
if user_max_chunk_size is not None:
|
212
|
+
user_max_chunk_size = min(
|
213
|
+
user_max_chunk_size, max(self.process_config.dataset_info.radio_dims[1] // 4, 10)
|
214
|
+
)
|
215
|
+
else:
|
216
|
+
user_max_chunk_size = max(self.process_config.dataset_info.radio_dims[1] // 4, 10)
|
217
|
+
|
183
218
|
self.cpu_max_chunk_size = self.estimate_chunk_size(
|
184
219
|
cpu_mem, self.process_config, chunk_step=1, user_max_chunk_size=user_max_chunk_size
|
185
220
|
)
|
@@ -219,12 +254,14 @@ class HelicalReconstructorRegridded:
|
|
219
254
|
reading_granularity=self.reading_granularity,
|
220
255
|
margin_v=self._margin_v,
|
221
256
|
span_info=self._span_info,
|
257
|
+
diag_zpro_run=self.diag_zpro_run,
|
222
258
|
)
|
223
259
|
|
224
260
|
required_mem_GB = required_mem / 1e9
|
225
261
|
if required_mem_GB > available_memory_GB:
|
226
262
|
break
|
227
263
|
last_good_chunk_size = chunk_size
|
264
|
+
|
228
265
|
if user_max_chunk_size is not None and chunk_size > user_max_chunk_size:
|
229
266
|
last_good_chunk_size = user_max_chunk_size
|
230
267
|
break
|
@@ -270,7 +307,7 @@ class HelicalReconstructorRegridded:
|
|
270
307
|
"""
|
271
308
|
raise ValueError(message)
|
272
309
|
|
273
|
-
if self._span_info.z_pix_per_proj[-1]
|
310
|
+
if self._span_info.z_pix_per_proj[-1] >= self._span_info.z_pix_per_proj[0]:
|
274
311
|
my_z_min = z_start + self.z_min
|
275
312
|
my_z_end = z_start + self.z_max
|
276
313
|
else:
|
@@ -283,7 +320,8 @@ class HelicalReconstructorRegridded:
|
|
283
320
|
print("my_z_min my_z_end ", my_z_min, my_z_end)
|
284
321
|
if my_z_min >= my_z_end:
|
285
322
|
message = f""" The requested vertical span, after translation to absolute doable heights would be {my_z_min, my_z_end}
|
286
|
-
is not doable (start>=end)
|
323
|
+
is not doable (start>=end).
|
324
|
+
Scans are often shorter than expected ThereFore : CONSIDER TO INCREASE angular_tolerance_steps
|
287
325
|
"""
|
288
326
|
raise ValueError(message)
|
289
327
|
|
@@ -406,7 +444,6 @@ class HelicalReconstructorRegridded:
|
|
406
444
|
phase_margin_pix=self._margin_v,
|
407
445
|
projection_angles_deg=angles_deg,
|
408
446
|
pixel_size_mm=self.process_config.dataset_info.pixel_size * 1.0e-3, # micron to mm
|
409
|
-
logger=self.logger,
|
410
447
|
require_redundancy=(redundancy_angle_deg > 0),
|
411
448
|
angular_tolerance_steps=self.process_config.nabu_config["reconstruction"]["angular_tolerance_steps"],
|
412
449
|
)
|
@@ -430,7 +467,8 @@ class HelicalReconstructorRegridded:
|
|
430
467
|
logger=self.logger,
|
431
468
|
phase_margin=task["phase_margin"],
|
432
469
|
reading_granularity=self.reading_granularity,
|
433
|
-
span_info=self._span_info
|
470
|
+
span_info=self._span_info,
|
471
|
+
diag_zpro_run=self.diag_zpro_run
|
434
472
|
# cuda_options=self.cuda_options
|
435
473
|
)
|
436
474
|
|
@@ -186,7 +186,7 @@ class SpanStrategy:
|
|
186
186
|
|
187
187
|
def get_informative_string(self):
|
188
188
|
doable_span_v = self.get_doable_span()
|
189
|
-
if self.z_pix_per_proj[-1]
|
189
|
+
if self.z_pix_per_proj[-1] >= self.z_pix_per_proj[-1]:
|
190
190
|
direction = "ascending"
|
191
191
|
else:
|
192
192
|
direction = "descending"
|
@@ -95,11 +95,14 @@ class TestGriddedAccumulator:
|
|
95
95
|
diagnostic_radios=reconstruction_space.diagnostic_radios,
|
96
96
|
diagnostic_weights=reconstruction_space.diagnostic_weights,
|
97
97
|
diagnostic_angles=reconstruction_space.diagnostic_proj_angle,
|
98
|
+
diagnostic_searched_angles_rad_clipped=reconstruction_space.diagnostic_searched_angles_rad_clipped,
|
99
|
+
diagnostic_zpix_transl=reconstruction_space.diagnostic_zpix_transl,
|
98
100
|
dark=self.dark,
|
99
101
|
flat_indexes=[0, 7501],
|
100
102
|
flats=self.flats,
|
101
103
|
weights=self.weights_field,
|
102
104
|
double_flat=self.double_flat,
|
105
|
+
diag_zpro_run=0,
|
103
106
|
)
|
104
107
|
|
105
108
|
# splitting in sub ranges of 100 projections
|
@@ -150,4 +153,5 @@ class TestGriddedAccumulator:
|
|
150
153
|
np.array((subr_start_z, subr_end_z), "i"),
|
151
154
|
np.array((dtasrc_start_z, dtasrc_end_z), "i"),
|
152
155
|
data_raw,
|
156
|
+
sub_total_prange_slice,
|
153
157
|
)
|
nabu/pipeline/params.py
CHANGED
@@ -107,15 +107,25 @@ cor_methods = {
|
|
107
107
|
"auto": "centered",
|
108
108
|
"centered": "centered",
|
109
109
|
"global": "global",
|
110
|
-
"sliding
|
110
|
+
"sino sliding window": "sino-sliding-window",
|
111
|
+
"sino-sliding-window": "sino-sliding-window",
|
111
112
|
"sliding window": "sliding-window",
|
112
|
-
"
|
113
|
+
"sliding-window": "sliding-window",
|
114
|
+
"sino growing window": "sino-growing-window",
|
115
|
+
"sino-growing-window": "sino-growing-window",
|
113
116
|
"growing window": "growing-window",
|
117
|
+
"growing-window": "growing-window",
|
114
118
|
"sino-coarse-to-fine": "sino-coarse-to-fine",
|
115
119
|
"composite-coarse-to-fine": "composite-coarse-to-fine",
|
116
120
|
"near": "composite-coarse-to-fine",
|
121
|
+
"fourier-angles": "fourier-angles",
|
122
|
+
"fourier angles": "fourier-angles",
|
123
|
+
"fourier-angle": "fourier-angles",
|
124
|
+
"fourier angle": "fourier-angles",
|
125
|
+
"octave-accurate": "octave-accurate",
|
117
126
|
}
|
118
127
|
|
128
|
+
|
119
129
|
tilt_methods = {
|
120
130
|
"1d-correlation": "1d-correlation",
|
121
131
|
"1dcorrelation": "1d-correlation",
|
@@ -128,6 +138,11 @@ rings_methods = {
|
|
128
138
|
"none": None,
|
129
139
|
"": None,
|
130
140
|
"munch": "munch",
|
141
|
+
"mean-subtraction": "mean-subtraction",
|
142
|
+
"mean_subtraction": "mean-subtraction",
|
143
|
+
"mean-division": "mean-division",
|
144
|
+
"mean_division": "mean-division",
|
145
|
+
"vo": "vo",
|
131
146
|
}
|
132
147
|
|
133
148
|
detector_distortion_correction_methods = {"none": None, "": None, "identity": "identity", "map_xz": "map_xz"}
|
@@ -140,3 +155,9 @@ radios_rotation_mode = {
|
|
140
155
|
"chunks": "chunk",
|
141
156
|
"full": "full",
|
142
157
|
}
|
158
|
+
|
159
|
+
exclude_projections_type = {
|
160
|
+
"indices": "indices",
|
161
|
+
"angular_range": "angular_range",
|
162
|
+
"angles": "angles",
|
163
|
+
}
|
nabu/pipeline/processconfig.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
import os
|
2
2
|
from .config import parse_nabu_config_file
|
3
|
-
from ..utils import
|
3
|
+
from ..utils import is_writeable
|
4
4
|
from ..resources.logger import Logger, PrinterLogger
|
5
5
|
from .config import validate_config
|
6
|
-
from ..resources.dataset_analyzer import analyze_dataset
|
6
|
+
from ..resources.dataset_analyzer import analyze_dataset
|
7
7
|
from .estimators import DetectorTiltEstimator
|
8
8
|
|
9
9
|
|
@@ -21,8 +21,6 @@ class ProcessConfigBase:
|
|
21
21
|
conf_fname=None,
|
22
22
|
conf_dict=None,
|
23
23
|
dataset_info=None,
|
24
|
-
checks=True,
|
25
|
-
remove_unused_radios=True,
|
26
24
|
create_logger=False,
|
27
25
|
):
|
28
26
|
"""
|
@@ -139,11 +137,8 @@ class ProcessConfigBase:
|
|
139
137
|
extra_options = {
|
140
138
|
"exclude_projections": self.nabu_config["dataset"]["exclude_projections"],
|
141
139
|
"hdf5_entry": self.nabu_config["dataset"]["hdf5_entry"],
|
140
|
+
"nx_version": self.nabu_config["dataset"]["nexus_version"],
|
142
141
|
}
|
143
|
-
if _tomoscan_has_nxversion: # legacy - should become "if True" soon
|
144
|
-
extra_options["nx_version"] = self.nabu_config["dataset"]["nexus_version"]
|
145
|
-
else:
|
146
|
-
self.logger.warning("Cannot use 'nx_version' for browsing dataset: need tomoscan > 0.6.0")
|
147
142
|
self.dataset_info = analyze_dataset(
|
148
143
|
self.nabu_config["dataset"]["location"], extra_options=extra_options, logger=self.logger
|
149
144
|
)
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import pytest
|
3
|
+
from nabu.io.utils import get_compacted_dataslices
|
4
|
+
from nabu.resources.dataset_analyzer import HDF5DatasetAnalyzer
|
5
|
+
from nabu.testutils import compare_arrays, __do_long_tests__, get_file
|
6
|
+
from nabu.io.reader import ChunkReader
|
7
|
+
|
8
|
+
|
9
|
+
@pytest.fixture(scope="class")
|
10
|
+
def bootstrap(request):
|
11
|
+
cls = request.cls
|
12
|
+
|
13
|
+
cls.dataset_fname = get_file("bamboo_reduced.nx")
|
14
|
+
cls.tol = 1e-7
|
15
|
+
|
16
|
+
|
17
|
+
def get_compacted_dataslices_as_sorted_tuples(reader):
|
18
|
+
slice_to_tuple = lambda s: (s.start, s.stop, s.step)
|
19
|
+
data_slices_tuples = list(
|
20
|
+
set(
|
21
|
+
[
|
22
|
+
slice_to_tuple(u.data_slice())
|
23
|
+
for u in get_compacted_dataslices(
|
24
|
+
reader.files, subsampling=reader.dataset_subsampling, begin=reader._files_begin_idx
|
25
|
+
).values()
|
26
|
+
]
|
27
|
+
)
|
28
|
+
)
|
29
|
+
return sorted(data_slices_tuples, key=lambda t: t[0])
|
30
|
+
|
31
|
+
|
32
|
+
@pytest.mark.skipif(not (__do_long_tests__), reason="Use __do_long_tests__ for this test")
|
33
|
+
@pytest.mark.usefixtures("bootstrap")
|
34
|
+
class TestChunkReader:
|
35
|
+
def test_subsampling(self):
|
36
|
+
"""
|
37
|
+
Test reading data from even/odd projections
|
38
|
+
"""
|
39
|
+
dataset_info = HDF5DatasetAnalyzer(self.dataset_fname)
|
40
|
+
|
41
|
+
# Read all projections, then only even-numbered ones, then odd-numbered ones
|
42
|
+
# Use the same object to hopefully use less memory (through garbage collection)
|
43
|
+
reader = ChunkReader(dataset_info.projections, dataset_subsampling=None)
|
44
|
+
reader.load_data()
|
45
|
+
first_sino_all = reader.data[:, 0, :].copy()
|
46
|
+
compacted_dataslices_all = get_compacted_dataslices_as_sorted_tuples(reader)
|
47
|
+
|
48
|
+
reader = ChunkReader(dataset_info.projections, dataset_subsampling=(2, 0))
|
49
|
+
reader.load_data()
|
50
|
+
first_sino_even = reader.data[:, 0, :].copy()
|
51
|
+
compacted_dataslices_even = get_compacted_dataslices_as_sorted_tuples(reader)
|
52
|
+
|
53
|
+
reader = ChunkReader(dataset_info.projections, dataset_subsampling=(2, 1))
|
54
|
+
reader.load_data()
|
55
|
+
first_sino_odd = reader.data[:, 0, :].copy()
|
56
|
+
compacted_dataslices_odd = get_compacted_dataslices_as_sorted_tuples(reader)
|
57
|
+
|
58
|
+
# from spire.utils import ims
|
59
|
+
# ims([first_sino_all[::2], first_sino_even, first_sino_all[::2]*1. - first_sino_even])
|
60
|
+
# ims([first_sino_all[::2], first_sino_even, first_sino_all[1::2]*1. - first_sino_odd])
|
61
|
+
|
62
|
+
# Check that the compacted data slices are correct
|
63
|
+
assert len(compacted_dataslices_all) == len(compacted_dataslices_even) == len(compacted_dataslices_odd)
|
64
|
+
for data_slice_all, data_slice_even, data_slice_odd in zip(
|
65
|
+
compacted_dataslices_all, compacted_dataslices_even, compacted_dataslices_odd
|
66
|
+
):
|
67
|
+
indices_all = np.arange(100000)[slice(*data_slice_all)]
|
68
|
+
indices_even = np.arange(100000)[slice(*data_slice_even)]
|
69
|
+
indices_odd = np.arange(100000)[slice(*data_slice_odd)]
|
70
|
+
assert indices_all[::2].size == indices_even.size
|
71
|
+
assert np.allclose(indices_all[::2], indices_even)
|
72
|
+
assert indices_all[1::2].size == indices_odd.size
|
73
|
+
assert np.allclose(indices_all[1::2], indices_odd)
|
74
|
+
|
75
|
+
is_close, max_err = compare_arrays(first_sino_all[::2, :], first_sino_even, self.tol, return_residual=True)
|
76
|
+
assert is_close, "ChunkReader: something wrong when subsampling for even projections. max_err=%.3e" % max_err
|
77
|
+
is_close, max_err = compare_arrays(first_sino_all[1::2, :], first_sino_odd, self.tol, return_residual=True)
|
78
|
+
assert is_close, "ChunkReader: something wrong when subsampling for odd projections. max_err=%.3e" % max_err
|
@@ -1,8 +1,18 @@
|
|
1
|
+
import os
|
1
2
|
import pytest
|
2
3
|
import numpy as np
|
3
|
-
from nabu.testutils import utilstest
|
4
|
+
from nabu.testutils import utilstest, __do_long_tests__
|
4
5
|
from nabu.resources.dataset_analyzer import HDF5DatasetAnalyzer
|
6
|
+
from nabu.resources.dataset_analyzer import analyze_dataset
|
7
|
+
from nabu.resources.utils import extract_parameters
|
5
8
|
from nabu.pipeline.estimators import CompositeCOREstimator
|
9
|
+
from nabu.pipeline.config import parse_nabu_config_file
|
10
|
+
from nabu.pipeline.estimators import SinoCORFinder, CORFinder
|
11
|
+
|
12
|
+
|
13
|
+
#
|
14
|
+
# Test CoR estimation with "composite-coarse-to-fine" (aka "near" in the legacy system vocable)
|
15
|
+
#
|
6
16
|
|
7
17
|
|
8
18
|
@pytest.fixture(scope="class")
|
@@ -16,9 +26,10 @@ def bootstrap(request):
|
|
16
26
|
cls.cor_pix = 1321.625
|
17
27
|
cls.abs_tol = 0.0001
|
18
28
|
cls.dataset_info = HDF5DatasetAnalyzer(dataset_downloaded_path)
|
19
|
-
cls.cor_options = """side="near"; near_pos = 300.0; near_width = 20.0 """
|
29
|
+
cls.cor_options = extract_parameters("""side="near"; near_pos = 300.0; near_width = 20.0 """, sep=";")
|
20
30
|
|
21
31
|
|
32
|
+
@pytest.mark.skipif(not (__do_long_tests__), reason="Need NABU_LONG_TESTS=1 for this test")
|
22
33
|
@pytest.mark.usefixtures("bootstrap")
|
23
34
|
class TestCompositeCorFinder:
|
24
35
|
def test(self):
|
@@ -29,3 +40,110 @@ class TestCompositeCorFinder:
|
|
29
40
|
cor_position = cor_finder.find_cor()
|
30
41
|
message = "Computed CoR %f " % cor_position + " and real CoR %f do not coincide" % self.cor_pix
|
31
42
|
assert np.isclose(self.cor_pix, cor_position, atol=self.abs_tol), message
|
43
|
+
|
44
|
+
|
45
|
+
@pytest.fixture(scope="class")
|
46
|
+
def bootstrap_bamboo_reduced(request):
|
47
|
+
cls = request.cls
|
48
|
+
cls.abs_tol = 0.2
|
49
|
+
# Dataset without estimated_cor_frm_motor (non regression test)
|
50
|
+
dataset_relpath = os.path.join("bamboo_reduced.nx")
|
51
|
+
dataset_downloaded_path = utilstest.getfile(dataset_relpath)
|
52
|
+
conf_relpath = os.path.join("bamboo_reduced.conf")
|
53
|
+
conf_downloaded_path = utilstest.getfile(conf_relpath)
|
54
|
+
cls.ds_std = analyze_dataset(dataset_downloaded_path)
|
55
|
+
cls.conf_std = parse_nabu_config_file(conf_downloaded_path)
|
56
|
+
|
57
|
+
# Dataset with estimated_cor_frm_motor
|
58
|
+
dataset_relpath = os.path.join("bamboo_reduced_bliss.nx")
|
59
|
+
dataset_downloaded_path = utilstest.getfile(dataset_relpath)
|
60
|
+
conf_relpath = os.path.join("bamboo_reduced_bliss.conf")
|
61
|
+
conf_downloaded_path = utilstest.getfile(conf_relpath)
|
62
|
+
cls.ds_bliss = analyze_dataset(dataset_downloaded_path)
|
63
|
+
cls.conf_bliss = parse_nabu_config_file(conf_downloaded_path)
|
64
|
+
|
65
|
+
|
66
|
+
@pytest.mark.skipif(not (__do_long_tests__), reason="need environment variable NABU_LONG_TESTS=1")
|
67
|
+
@pytest.mark.usefixtures("bootstrap_bamboo_reduced")
|
68
|
+
class TestCorNearPos:
|
69
|
+
true_cor = 339.486
|
70
|
+
|
71
|
+
def test_cor_sliding_standard(self):
|
72
|
+
cor_options = extract_parameters(self.conf_std["reconstruction"].get("cor_options", None), sep=";")
|
73
|
+
finder = CORFinder("sliding-window", self.ds_std, do_flatfield=True, cor_options=cor_options)
|
74
|
+
cor = finder.find_cor()
|
75
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
76
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
77
|
+
|
78
|
+
cor_options.update({"side": "from_file"})
|
79
|
+
finder = CORFinder("sliding-window", self.ds_std, do_flatfield=True, cor_options=cor_options)
|
80
|
+
cor = finder.find_cor()
|
81
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
82
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
83
|
+
|
84
|
+
cor_options.update({"side": "center"})
|
85
|
+
finder = CORFinder("sliding-window", self.ds_std, do_flatfield=True, cor_options=cor_options)
|
86
|
+
cor = finder.find_cor()
|
87
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
88
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
89
|
+
|
90
|
+
def test_cor_fourier_angles_standard(self):
|
91
|
+
cor_options = extract_parameters(self.conf_std["reconstruction"].get("cor_options", None), sep=";")
|
92
|
+
finder = SinoCORFinder("fourier-angles", self.ds_std, do_flatfield=True, cor_options=cor_options)
|
93
|
+
cor = finder.find_cor()
|
94
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
95
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
96
|
+
|
97
|
+
# Checks that it works though no data in NX
|
98
|
+
cor_options.update({"side": "from_file"})
|
99
|
+
finder = SinoCORFinder("fourier-angles", self.ds_std, do_flatfield=True, cor_options=cor_options)
|
100
|
+
cor = finder.find_cor()
|
101
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
102
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
103
|
+
|
104
|
+
cor_options.update({"side": "center"})
|
105
|
+
finder = SinoCORFinder("fourier-angles", self.ds_std, do_flatfield=True, cor_options=cor_options)
|
106
|
+
cor = finder.find_cor()
|
107
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
108
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
109
|
+
|
110
|
+
def test_cor_sliding_bliss(self):
|
111
|
+
cor_options = extract_parameters(self.conf_bliss["reconstruction"].get("cor_options", None), sep=";")
|
112
|
+
finder = CORFinder("sliding-window", self.ds_bliss, do_flatfield=True, cor_options=cor_options)
|
113
|
+
cor = finder.find_cor()
|
114
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
115
|
+
print(message)
|
116
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
117
|
+
|
118
|
+
cor_options.update({"side": "from_file"})
|
119
|
+
finder = CORFinder("sliding-window", self.ds_bliss, do_flatfield=True, cor_options=cor_options)
|
120
|
+
cor = finder.find_cor()
|
121
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
122
|
+
print(message)
|
123
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
124
|
+
|
125
|
+
cor_options.update({"side": "center"})
|
126
|
+
finder = CORFinder("sliding-window", self.ds_bliss, do_flatfield=True, cor_options=cor_options)
|
127
|
+
cor = finder.find_cor()
|
128
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
129
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
130
|
+
|
131
|
+
def test_cor_fourier_angles_bliss(self):
|
132
|
+
cor_options = extract_parameters(self.conf_bliss["reconstruction"].get("cor_options", None), sep=";")
|
133
|
+
finder = SinoCORFinder("fourier-angles", self.ds_bliss, do_flatfield=True, cor_options=cor_options)
|
134
|
+
cor = finder.find_cor()
|
135
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
136
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
137
|
+
|
138
|
+
# Checks that it works though no data in NX
|
139
|
+
cor_options.update({"side": "from_file"})
|
140
|
+
finder = SinoCORFinder("fourier-angles", self.ds_bliss, do_flatfield=True, cor_options=cor_options)
|
141
|
+
cor = finder.find_cor()
|
142
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
143
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
144
|
+
|
145
|
+
cor_options.update({"side": "center"})
|
146
|
+
finder = SinoCORFinder("fourier-angles", self.ds_bliss, do_flatfield=True, cor_options=cor_options)
|
147
|
+
cor = finder.find_cor()
|
148
|
+
message = f"Computed CoR {cor} and expected CoR {self.true_cor} do not coincide. Near_pos options was set to {cor_options.get('near_pos',None)}."
|
149
|
+
assert np.isclose(self.true_cor, cor, atol=self.abs_tol), message
|
nabu/pipeline/utils.py
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
from ..utils import deprecated_class
|
2
|
+
from .config_validators import str2bool
|
3
|
+
from dataclasses import dataclass
|
4
|
+
import os
|
2
5
|
|
3
6
|
#
|
4
7
|
# Decorators and callback mechanism
|
@@ -89,3 +92,25 @@ from .writer import WriterManager
|
|
89
92
|
WriterConfigurator = deprecated_class("WriterConfigurator moved to nabu.pipeline.writer.WriterManager", do_print=True)(
|
90
93
|
WriterManager
|
91
94
|
)
|
95
|
+
|
96
|
+
|
97
|
+
@dataclass
|
98
|
+
class EnvSettings:
|
99
|
+
"""This class centralises the definitions, possibly documentation, and access to environnt variable
|
100
|
+
driven settings.
|
101
|
+
It is meant to be used in the following way:
|
102
|
+
from nabu.utils import nabu_env_settings
|
103
|
+
if not nabu_env_settings.skip_tomoscan_checks:
|
104
|
+
do something
|
105
|
+
"""
|
106
|
+
|
107
|
+
skip_tomoscan_checks: bool = False
|
108
|
+
|
109
|
+
|
110
|
+
def _get_nabu_environment_variables():
|
111
|
+
nabu_env_settings = EnvSettings()
|
112
|
+
nabu_env_settings.skip_tomoscan_checks = str2bool(os.getenv("SKIP_TOMOSCAN_CHECK", "0"))
|
113
|
+
return nabu_env_settings
|
114
|
+
|
115
|
+
|
116
|
+
nabu_env_settings = _get_nabu_environment_variables()
|
nabu/pipeline/writer.py
CHANGED
@@ -124,6 +124,7 @@ class WriterManager:
|
|
124
124
|
if self.file_format == "hdf5":
|
125
125
|
writer_kwargs["data_path"] = self.metadata.get("entry", "entry")
|
126
126
|
writer_kwargs["process_name"] = self.metadata.get("process_name", "reconstruction")
|
127
|
+
writer_kwargs["create_subfolder"] = self.extra_options.get("create_subfolder", True)
|
127
128
|
elif self.file_format == "jp2":
|
128
129
|
writer_kwargs["cratios"] = self.metadata.get("jpeg2000_compression_ratio", None)
|
129
130
|
writer_kwargs["clip_values"] = self.metadata.get("float_clip_values", None)
|
@@ -135,6 +136,7 @@ class WriterManager:
|
|
135
136
|
),
|
136
137
|
"overwrite": self.overwrite,
|
137
138
|
"append": self.extra_options.get("single_output_file_initialized", False),
|
139
|
+
"hst_metadata": self.extra_options.get("raw_vol_metadata", {}),
|
138
140
|
}
|
139
141
|
else:
|
140
142
|
raise ValueError("Unsupported file format: %s" % self.file_format)
|
nabu/preproc/ccd_cuda.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
from typing import Union
|
2
1
|
import numpy as np
|
3
2
|
from ..preproc.ccd import CCDFilter, Log
|
4
|
-
from ..
|
5
|
-
from ..cuda.medfilt import MedianFilter
|
3
|
+
from ..processing.medfilt_cuda import MedianFilter
|
6
4
|
from ..utils import get_cuda_srcfile, updiv, deprecated_class
|
5
|
+
from ..cuda.utils import __has_pycuda__
|
6
|
+
|
7
|
+
if __has_pycuda__:
|
8
|
+
from ..cuda.kernel import CudaKernel
|
7
9
|
|
8
10
|
# COMPAT.
|
9
11
|
from .flatfield_cuda import (
|
@@ -27,11 +29,11 @@ FlatFieldDataUrls = deprecated_class(
|
|
27
29
|
class CudaCCDFilter(CCDFilter):
|
28
30
|
def __init__(
|
29
31
|
self,
|
30
|
-
radios_shape
|
31
|
-
correction_type
|
32
|
-
median_clip_thresh
|
32
|
+
radios_shape,
|
33
|
+
correction_type="median_clip",
|
34
|
+
median_clip_thresh=0.1,
|
33
35
|
abs_diff=False,
|
34
|
-
cuda_options
|
36
|
+
cuda_options=None,
|
35
37
|
):
|
36
38
|
"""
|
37
39
|
Initialize a CudaCCDCorrection instance.
|