nabu 2023.2.1__py3-none-any.whl → 2024.1.0rc3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. doc/conf.py +1 -1
  2. doc/doc_config.py +32 -0
  3. nabu/__init__.py +2 -1
  4. nabu/app/bootstrap_stitching.py +1 -1
  5. nabu/app/cli_configs.py +122 -2
  6. nabu/app/composite_cor.py +27 -2
  7. nabu/app/correct_rot.py +70 -0
  8. nabu/app/create_distortion_map_from_poly.py +42 -18
  9. nabu/app/diag_to_pix.py +358 -0
  10. nabu/app/diag_to_rot.py +449 -0
  11. nabu/app/generate_header.py +4 -3
  12. nabu/app/histogram.py +2 -2
  13. nabu/app/multicor.py +6 -1
  14. nabu/app/parse_reconstruction_log.py +151 -0
  15. nabu/app/prepare_weights_double.py +83 -22
  16. nabu/app/reconstruct.py +5 -1
  17. nabu/app/reconstruct_helical.py +7 -0
  18. nabu/app/reduce_dark_flat.py +6 -3
  19. nabu/app/rotate.py +4 -4
  20. nabu/app/stitching.py +16 -2
  21. nabu/app/tests/test_reduce_dark_flat.py +18 -2
  22. nabu/app/validator.py +4 -4
  23. nabu/cuda/convolution.py +8 -376
  24. nabu/cuda/fft.py +4 -0
  25. nabu/cuda/kernel.py +4 -4
  26. nabu/cuda/medfilt.py +5 -158
  27. nabu/cuda/padding.py +5 -71
  28. nabu/cuda/processing.py +23 -2
  29. nabu/cuda/src/ElementOp.cu +78 -0
  30. nabu/cuda/src/backproj.cu +28 -2
  31. nabu/cuda/src/fourier_wavelets.cu +2 -2
  32. nabu/cuda/src/normalization.cu +23 -0
  33. nabu/cuda/src/padding.cu +2 -2
  34. nabu/cuda/src/transpose.cu +16 -0
  35. nabu/cuda/utils.py +39 -0
  36. nabu/estimation/alignment.py +10 -1
  37. nabu/estimation/cor.py +808 -38
  38. nabu/estimation/cor_sino.py +7 -9
  39. nabu/estimation/tests/test_cor.py +85 -3
  40. nabu/io/reader.py +26 -18
  41. nabu/io/tests/test_cast_volume.py +3 -3
  42. nabu/io/tests/test_detector_distortion.py +3 -3
  43. nabu/io/tiffwriter_zmm.py +2 -2
  44. nabu/io/utils.py +14 -4
  45. nabu/io/writer.py +5 -3
  46. nabu/misc/fftshift.py +6 -0
  47. nabu/misc/histogram.py +5 -285
  48. nabu/misc/histogram_cuda.py +8 -104
  49. nabu/misc/kernel_base.py +3 -121
  50. nabu/misc/padding_base.py +5 -69
  51. nabu/misc/processing_base.py +3 -107
  52. nabu/misc/rotation.py +5 -62
  53. nabu/misc/rotation_cuda.py +5 -65
  54. nabu/misc/transpose.py +6 -0
  55. nabu/misc/unsharp.py +3 -78
  56. nabu/misc/unsharp_cuda.py +5 -52
  57. nabu/misc/unsharp_opencl.py +8 -85
  58. nabu/opencl/fft.py +6 -0
  59. nabu/opencl/kernel.py +21 -6
  60. nabu/opencl/padding.py +5 -72
  61. nabu/opencl/processing.py +27 -5
  62. nabu/opencl/src/backproj.cl +3 -3
  63. nabu/opencl/src/fftshift.cl +65 -12
  64. nabu/opencl/src/padding.cl +2 -2
  65. nabu/opencl/src/roll.cl +96 -0
  66. nabu/opencl/src/transpose.cl +16 -0
  67. nabu/pipeline/config_validators.py +63 -3
  68. nabu/pipeline/dataset_validator.py +2 -2
  69. nabu/pipeline/estimators.py +193 -35
  70. nabu/pipeline/fullfield/chunked.py +34 -17
  71. nabu/pipeline/fullfield/chunked_cuda.py +7 -5
  72. nabu/pipeline/fullfield/computations.py +48 -13
  73. nabu/pipeline/fullfield/nabu_config.py +13 -13
  74. nabu/pipeline/fullfield/processconfig.py +10 -5
  75. nabu/pipeline/fullfield/reconstruction.py +1 -2
  76. nabu/pipeline/helical/fbp.py +5 -0
  77. nabu/pipeline/helical/filtering.py +12 -9
  78. nabu/pipeline/helical/gridded_accumulator.py +179 -33
  79. nabu/pipeline/helical/helical_chunked_regridded.py +262 -151
  80. nabu/pipeline/helical/helical_chunked_regridded_cuda.py +4 -11
  81. nabu/pipeline/helical/helical_reconstruction.py +56 -18
  82. nabu/pipeline/helical/span_strategy.py +1 -1
  83. nabu/pipeline/helical/tests/test_accumulator.py +4 -0
  84. nabu/pipeline/params.py +23 -2
  85. nabu/pipeline/processconfig.py +3 -8
  86. nabu/pipeline/tests/test_chunk_reader.py +78 -0
  87. nabu/pipeline/tests/test_estimators.py +120 -2
  88. nabu/pipeline/utils.py +25 -0
  89. nabu/pipeline/writer.py +2 -0
  90. nabu/preproc/ccd_cuda.py +9 -7
  91. nabu/preproc/ctf.py +21 -26
  92. nabu/preproc/ctf_cuda.py +25 -25
  93. nabu/preproc/double_flatfield.py +14 -2
  94. nabu/preproc/double_flatfield_cuda.py +7 -11
  95. nabu/preproc/flatfield_cuda.py +23 -27
  96. nabu/preproc/phase.py +19 -24
  97. nabu/preproc/phase_cuda.py +21 -21
  98. nabu/preproc/shift_cuda.py +58 -28
  99. nabu/preproc/tests/test_ctf.py +5 -5
  100. nabu/preproc/tests/test_double_flatfield.py +2 -2
  101. nabu/preproc/tests/test_vshift.py +13 -2
  102. nabu/processing/__init__.py +0 -0
  103. nabu/processing/convolution_cuda.py +375 -0
  104. nabu/processing/fft_base.py +163 -0
  105. nabu/processing/fft_cuda.py +256 -0
  106. nabu/processing/fft_opencl.py +54 -0
  107. nabu/processing/fftshift.py +134 -0
  108. nabu/processing/histogram.py +286 -0
  109. nabu/processing/histogram_cuda.py +103 -0
  110. nabu/processing/kernel_base.py +126 -0
  111. nabu/processing/medfilt_cuda.py +159 -0
  112. nabu/processing/muladd.py +29 -0
  113. nabu/processing/muladd_cuda.py +68 -0
  114. nabu/processing/padding_base.py +71 -0
  115. nabu/processing/padding_cuda.py +75 -0
  116. nabu/processing/padding_opencl.py +77 -0
  117. nabu/processing/processing_base.py +123 -0
  118. nabu/processing/roll_opencl.py +64 -0
  119. nabu/processing/rotation.py +63 -0
  120. nabu/processing/rotation_cuda.py +66 -0
  121. nabu/processing/tests/__init__.py +0 -0
  122. nabu/processing/tests/test_fft.py +268 -0
  123. nabu/processing/tests/test_fftshift.py +71 -0
  124. nabu/{misc → processing}/tests/test_histogram.py +2 -4
  125. nabu/{cuda → processing}/tests/test_medfilt.py +1 -1
  126. nabu/processing/tests/test_muladd.py +54 -0
  127. nabu/{cuda → processing}/tests/test_padding.py +119 -75
  128. nabu/processing/tests/test_roll.py +63 -0
  129. nabu/{misc → processing}/tests/test_rotation.py +3 -2
  130. nabu/processing/tests/test_transpose.py +72 -0
  131. nabu/{misc → processing}/tests/test_unsharp.py +41 -8
  132. nabu/processing/transpose.py +126 -0
  133. nabu/processing/unsharp.py +79 -0
  134. nabu/processing/unsharp_cuda.py +53 -0
  135. nabu/processing/unsharp_opencl.py +75 -0
  136. nabu/reconstruction/fbp.py +34 -10
  137. nabu/reconstruction/fbp_base.py +35 -16
  138. nabu/reconstruction/fbp_opencl.py +7 -12
  139. nabu/reconstruction/filtering.py +2 -2
  140. nabu/reconstruction/filtering_cuda.py +13 -14
  141. nabu/reconstruction/filtering_opencl.py +3 -4
  142. nabu/reconstruction/projection.py +2 -0
  143. nabu/reconstruction/rings.py +158 -1
  144. nabu/reconstruction/rings_cuda.py +218 -58
  145. nabu/reconstruction/sinogram_cuda.py +16 -12
  146. nabu/reconstruction/tests/test_deringer.py +116 -14
  147. nabu/reconstruction/tests/test_fbp.py +22 -31
  148. nabu/reconstruction/tests/test_filtering.py +11 -2
  149. nabu/resources/dataset_analyzer.py +89 -26
  150. nabu/resources/nxflatfield.py +2 -2
  151. nabu/resources/tests/test_nxflatfield.py +1 -1
  152. nabu/resources/utils.py +9 -2
  153. nabu/stitching/alignment.py +184 -0
  154. nabu/stitching/config.py +241 -39
  155. nabu/stitching/definitions.py +6 -0
  156. nabu/stitching/frame_composition.py +4 -2
  157. nabu/stitching/overlap.py +99 -3
  158. nabu/stitching/sample_normalization.py +60 -0
  159. nabu/stitching/slurm_utils.py +10 -10
  160. nabu/stitching/tests/test_alignment.py +99 -0
  161. nabu/stitching/tests/test_config.py +16 -1
  162. nabu/stitching/tests/test_overlap.py +68 -2
  163. nabu/stitching/tests/test_sample_normalization.py +49 -0
  164. nabu/stitching/tests/test_slurm_utils.py +5 -5
  165. nabu/stitching/tests/test_utils.py +3 -33
  166. nabu/stitching/tests/test_z_stitching.py +391 -22
  167. nabu/stitching/utils.py +144 -202
  168. nabu/stitching/z_stitching.py +309 -126
  169. nabu/testutils.py +18 -0
  170. nabu/thirdparty/tomocupy_remove_stripe.py +586 -0
  171. nabu/utils.py +32 -6
  172. {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/LICENSE +1 -1
  173. {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/METADATA +5 -5
  174. nabu-2024.1.0rc3.dist-info/RECORD +296 -0
  175. {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/WHEEL +1 -1
  176. {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/entry_points.txt +5 -1
  177. nabu/conftest.py +0 -14
  178. nabu/opencl/fftshift.py +0 -92
  179. nabu/opencl/tests/test_fftshift.py +0 -55
  180. nabu/opencl/tests/test_padding.py +0 -84
  181. nabu-2023.2.1.dist-info/RECORD +0 -252
  182. /nabu/cuda/src/{fftshift.cu → dfi_fftshift.cu} +0 -0
  183. {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/top_level.txt +0 -0
@@ -1,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 ...reconstruction.sinogram import SinoBuilder, SinoNormalization
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__, replace_array_memory
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, basename, dirname
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, NXProcessWriter
7
+ from ...io.writer import merge_hdf5_files
13
8
  from ...cuda.utils import collect_cuda_gpus
14
- from ...preproc.phase import compute_paganin_margin
15
- from ...misc.histogram import PartialHistogram, add_last_bin, hist_as_2Darray
16
- from .span_strategy import SpanStrategy
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, variable_idxlen_sort, FullFieldReconstructor
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
- self._margin_v, self._margin_h = self._compute_phase_margin()
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
- self.z_min = int(round(z_start * (1 - z_fract_min) + z_max * z_fract_min))
149
- self.z_max = int(round(z_start * (1 - z_fract_max) + z_max * z_fract_max)) + 1
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] > self._span_info.z_pix_per_proj[0]:
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] > 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-window": "sliding-window",
110
+ "sino sliding window": "sino-sliding-window",
111
+ "sino-sliding-window": "sino-sliding-window",
111
112
  "sliding window": "sliding-window",
112
- "growing-window": "growing-window",
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
+ }
@@ -1,9 +1,9 @@
1
1
  import os
2
2
  from .config import parse_nabu_config_file
3
- from ..utils import deprecation_warning, is_writeable
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, _tomoscan_has_nxversion
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 ..cuda.kernel import CudaKernel
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: tuple,
31
- correction_type: str = "median_clip",
32
- median_clip_thresh: float = 0.1,
32
+ radios_shape,
33
+ correction_type="median_clip",
34
+ median_clip_thresh=0.1,
33
35
  abs_diff=False,
34
- cuda_options: Union[dict, None] = None,
36
+ cuda_options=None,
35
37
  ):
36
38
  """
37
39
  Initialize a CudaCCDCorrection instance.