tomwer 1.4.0rc0__py3-none-any.whl → 1.4.0rc1__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.
- orangecontrib/tomwer/tutorials/test_cor.ows +3 -3
- orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +6 -14
- orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +4 -2
- tomwer/app/axis.py +0 -3
- tomwer/app/multipag.py +11 -3
- tomwer/core/process/reconstruction/axis/axis.py +27 -736
- tomwer/core/process/reconstruction/axis/mode.py +86 -24
- tomwer/core/process/reconstruction/axis/params.py +127 -138
- tomwer/core/process/reconstruction/axis/side.py +8 -0
- tomwer/core/process/reconstruction/nabu/nabuscores.py +17 -20
- tomwer/core/process/reconstruction/nabu/nabuslices.py +5 -1
- tomwer/core/process/reconstruction/saaxis/saaxis.py +4 -4
- tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +4 -4
- tomwer/core/process/reconstruction/tests/test_axis.py +1 -1
- tomwer/core/process/reconstruction/tests/test_utils.py +4 -4
- tomwer/core/process/reconstruction/utils/cor.py +8 -4
- tomwer/core/process/tests/test_nabu.py +1 -3
- tomwer/core/scan/scanbase.py +4 -4
- tomwer/core/scan/tests/test_process_registration.py +0 -18
- tomwer/gui/fonts.py +5 -0
- tomwer/gui/reconstruction/axis/AxisMainWindow.py +20 -9
- tomwer/gui/reconstruction/axis/AxisOptionsWidget.py +239 -79
- tomwer/gui/reconstruction/axis/AxisSettingsWidget.py +38 -17
- tomwer/gui/reconstruction/axis/AxisWidget.py +16 -8
- tomwer/gui/reconstruction/axis/CalculationWidget.py +40 -200
- tomwer/gui/reconstruction/axis/ControlWidget.py +10 -2
- tomwer/gui/reconstruction/axis/EstimatedCORWidget.py +383 -0
- tomwer/gui/reconstruction/axis/EstimatedCorComboBox.py +118 -0
- tomwer/gui/reconstruction/axis/InputWidget.py +11 -155
- tomwer/gui/reconstruction/saaxis/corrangeselector.py +19 -10
- tomwer/gui/reconstruction/scores/scoreplot.py +5 -2
- tomwer/gui/reconstruction/tests/test_nabu.py +8 -0
- tomwer/gui/stitching/z_stitching/fineestimation.py +1 -1
- tomwer/gui/tests/test_axis_gui.py +31 -15
- tomwer/synctools/stacks/reconstruction/axis.py +5 -23
- tomwer/synctools/stacks/reconstruction/dkrefcopy.py +1 -1
- tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
- tomwer/synctools/stacks/reconstruction/normalization.py +1 -1
- tomwer/synctools/stacks/reconstruction/saaxis.py +1 -1
- tomwer/synctools/stacks/reconstruction/sadeltabeta.py +1 -1
- tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_axis.py +0 -16
- tomwer/tests/test_ewoks/test_single_node_execution.py +1 -1
- tomwer/tests/test_ewoks/test_workflows.py +1 -1
- tomwer/version.py +1 -1
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/METADATA +2 -2
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/RECORD +50 -47
- tomwer/core/process/tests/test_axis.py +0 -231
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/LICENSE +0 -0
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/WHEEL +0 -0
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/entry_points.txt +0 -0
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,23 +1,14 @@
|
|
1
|
-
"""contain the
|
1
|
+
"""contain the AxisTask class
|
2
2
|
"""
|
3
3
|
|
4
4
|
from __future__ import annotations
|
5
5
|
|
6
6
|
import logging
|
7
7
|
|
8
|
-
import
|
9
|
-
from nabu.estimation.cor import (
|
10
|
-
CenterOfRotation,
|
11
|
-
CenterOfRotationAdaptiveSearch,
|
12
|
-
CenterOfRotationGrowingWindow,
|
13
|
-
CenterOfRotationSlidingWindow,
|
14
|
-
CenterOfRotationOctaveAccurate,
|
15
|
-
)
|
16
|
-
from nabu.pipeline.estimators import SinoCORFinder, CORFinder
|
8
|
+
from nabu.pipeline.estimators import estimate_cor
|
17
9
|
from nabu.resources.nxflatfield import update_dataset_info_flats_darks
|
18
10
|
from processview.core.manager import DatasetState, ProcessManager
|
19
11
|
from processview.core.superviseprocess import SuperviseProcess
|
20
|
-
from tomwer.core.utils.deprecation import deprecated_warning
|
21
12
|
|
22
13
|
import tomwer.version
|
23
14
|
from tomwer.core.process.reconstruction.utils.cor import absolute_pos_to_relative
|
@@ -42,19 +33,8 @@ from .params import (
|
|
42
33
|
)
|
43
34
|
from .projectiontype import ProjectionType
|
44
35
|
|
45
|
-
try:
|
46
|
-
from nabu.pipeline.estimators import CompositeCOREstimator
|
47
|
-
except ImportError:
|
48
|
-
has_composite_cor_finder = False
|
49
|
-
else:
|
50
|
-
has_composite_cor_finder = True
|
51
|
-
from silx.io.url import DataUrl
|
52
|
-
from silx.io.utils import h5py_read_dataset
|
53
|
-
from silx.io.utils import open as open_hdf5
|
54
|
-
|
55
36
|
_logger = logging.getLogger(__name__)
|
56
|
-
|
57
|
-
_logger.warning("No composite cor finder found at nabu level")
|
37
|
+
|
58
38
|
# vertically, work on a window having only a percentage of the frame.
|
59
39
|
pc_height = 10.0 / 100.0
|
60
40
|
# horizontally. Global method supposes the COR is more or less in center
|
@@ -105,191 +85,6 @@ def adapt_tomwer_scan_to_nabu(scan: TomwerScanBase):
|
|
105
85
|
return dataset_infos
|
106
86
|
|
107
87
|
|
108
|
-
def compute_cor_nabu_growing_window(
|
109
|
-
radio_1: numpy.ndarray,
|
110
|
-
radio_2: numpy.ndarray,
|
111
|
-
side: str,
|
112
|
-
padding_mode,
|
113
|
-
flip_frame_2_lr=True,
|
114
|
-
horz_fft_width=False,
|
115
|
-
):
|
116
|
-
"""
|
117
|
-
Call nabu.preproc.alignement.CenterOfRotationGrowingWindow.find_shift
|
118
|
-
|
119
|
-
:param radio_1:
|
120
|
-
:param radio_2:
|
121
|
-
:param padding_mode: padding mode
|
122
|
-
:param side: side of the cor
|
123
|
-
:param flip_frame_2_lr: if True will left-right flip the second frame
|
124
|
-
:param horz_fft_width:
|
125
|
-
|
126
|
-
:return:
|
127
|
-
"""
|
128
|
-
nabu_class = CenterOfRotationGrowingWindow(horz_fft_width=horz_fft_width)
|
129
|
-
# value return is relative
|
130
|
-
res = nabu_class.find_shift(
|
131
|
-
img_1=radio_1,
|
132
|
-
img_2=numpy.fliplr(radio_2) if flip_frame_2_lr else radio_2,
|
133
|
-
side=side,
|
134
|
-
roi_yxhw=None,
|
135
|
-
padding_mode=padding_mode,
|
136
|
-
median_filt_shape=None,
|
137
|
-
)
|
138
|
-
if isinstance(res, numpy.ndarray):
|
139
|
-
if len(res) == 1:
|
140
|
-
return res[0]
|
141
|
-
else:
|
142
|
-
raise ValueError(
|
143
|
-
"nabu rsult is expected to be a scalar, numpy array found. Please upgrade nabu this issue is expected to be solved"
|
144
|
-
)
|
145
|
-
else:
|
146
|
-
return res
|
147
|
-
|
148
|
-
|
149
|
-
def compute_cor_nabu_growing_window_radios(
|
150
|
-
scan: TomwerScanBase,
|
151
|
-
) -> float | None:
|
152
|
-
"""
|
153
|
-
Call nabu.preproc.alignement.CenterOfRotationGrowingWindow.find_shift
|
154
|
-
|
155
|
-
:param scan: scan for which we want to get the reconstruction parameters
|
156
|
-
"""
|
157
|
-
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
158
|
-
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
159
|
-
|
160
|
-
projection_angles = scan.get_proj_angle_url()
|
161
|
-
projection_angles_i = {
|
162
|
-
value.path(): key for key, value in projection_angles.items()
|
163
|
-
}
|
164
|
-
url_radio_1, url_radio_2 = AxisTask.get_inputs_urls(scan=scan)
|
165
|
-
angle_radio_1 = float(projection_angles_i[url_radio_1.url.path()])
|
166
|
-
angle_radio_2 = float(projection_angles_i[url_radio_2.url.path()])
|
167
|
-
radio_angles = tuple(numpy.radians((angle_radio_1, angle_radio_2)))
|
168
|
-
|
169
|
-
corfinder = CORFinder(
|
170
|
-
dataset_info=adapt_tomwer_scan_to_nabu(scan),
|
171
|
-
method="growing-window",
|
172
|
-
do_flatfield=has_darks and has_flats,
|
173
|
-
cor_options=scan.axis_params.get_nabu_cor_options_as_dict(),
|
174
|
-
radio_angles=radio_angles,
|
175
|
-
logger=_logger,
|
176
|
-
)
|
177
|
-
res = corfinder.find_cor() # Returns absolute cor
|
178
|
-
if isinstance(res, numpy.ndarray):
|
179
|
-
if len(res) == 1:
|
180
|
-
res = res[0]
|
181
|
-
else:
|
182
|
-
raise ValueError(
|
183
|
-
"nabu rsult is expected to be a scalar, numpy array found. Please upgrade nabu this issue is expected to be solved"
|
184
|
-
)
|
185
|
-
|
186
|
-
return _absolute_pos_to_relative_with_warning(
|
187
|
-
absolute_pos=res, det_width=scan.dim_1
|
188
|
-
)
|
189
|
-
|
190
|
-
|
191
|
-
def compute_cor_nabu_growing_window_sinogram(
|
192
|
-
scan: TomwerScanBase,
|
193
|
-
) -> float | None:
|
194
|
-
"""
|
195
|
-
Call nabu.preproc.alignement.CenterOfRotationGrowingWindow.find_shift
|
196
|
-
|
197
|
-
:param scan:
|
198
|
-
"""
|
199
|
-
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
200
|
-
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
201
|
-
|
202
|
-
corfinder = SinoCORFinder(
|
203
|
-
dataset_info=adapt_tomwer_scan_to_nabu(scan),
|
204
|
-
method="sino-growing-window",
|
205
|
-
slice_idx=scan.axis_params.sinogram_line or "middle",
|
206
|
-
subsampling=scan.axis_params.sinogram_subsampling,
|
207
|
-
do_flatfield=has_darks and has_flats,
|
208
|
-
cor_options=scan.axis_params.get_nabu_cor_options_as_dict(),
|
209
|
-
logger=_logger,
|
210
|
-
)
|
211
|
-
res = corfinder.find_cor()
|
212
|
-
if isinstance(res, numpy.ndarray):
|
213
|
-
if len(res) == 1:
|
214
|
-
res = res[0]
|
215
|
-
else:
|
216
|
-
raise ValueError(
|
217
|
-
"nabu rsult is expected to be a scalar, numpy array found. Please upgrade nabu this issue is expected to be solved"
|
218
|
-
)
|
219
|
-
|
220
|
-
return _absolute_pos_to_relative_with_warning(
|
221
|
-
absolute_pos=res, det_width=scan.dim_1
|
222
|
-
)
|
223
|
-
|
224
|
-
|
225
|
-
def compute_scan_sino_coarse_to_fine(scan):
|
226
|
-
"""
|
227
|
-
Compute center of rotation from `sino-coarse-to-fine` algorithm for given
|
228
|
-
scan
|
229
|
-
:param scan:
|
230
|
-
:return:
|
231
|
-
"""
|
232
|
-
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
233
|
-
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
234
|
-
|
235
|
-
corfinder = SinoCORFinder(
|
236
|
-
dataset_info=adapt_tomwer_scan_to_nabu(scan),
|
237
|
-
method=AxisMode.sino_coarse_to_fine.value,
|
238
|
-
slice_idx=scan.axis_params.sinogram_line or "middle",
|
239
|
-
subsampling=scan.axis_params.sinogram_subsampling,
|
240
|
-
do_flatfield=has_darks and has_flats,
|
241
|
-
cor_options=scan.axis_params.get_nabu_cor_options_as_dict(),
|
242
|
-
logger=_logger,
|
243
|
-
)
|
244
|
-
res = corfinder.find_cor()
|
245
|
-
return _absolute_pos_to_relative_with_warning(
|
246
|
-
absolute_pos=res, det_width=scan.dim_1
|
247
|
-
)
|
248
|
-
|
249
|
-
|
250
|
-
def compute_scan_composite_coarse_to_fine(scan: TomwerScanBase):
|
251
|
-
"""
|
252
|
-
Compute center of rotation from `sino-coarse-to-fine` algorithm for given
|
253
|
-
scan
|
254
|
-
:param scan:
|
255
|
-
:return:
|
256
|
-
"""
|
257
|
-
if not has_composite_cor_finder:
|
258
|
-
_logger.error("unable to find nabu CompositeCOREstimator")
|
259
|
-
return None
|
260
|
-
|
261
|
-
(
|
262
|
-
theta,
|
263
|
-
n_subsampling_y,
|
264
|
-
oversampling,
|
265
|
-
take_log,
|
266
|
-
near_pos,
|
267
|
-
near_width,
|
268
|
-
) = get_composite_options(scan)
|
269
|
-
|
270
|
-
# as the new corfinder is not yet merged in the main branch
|
271
|
-
# allow some tolerance for the "side" argument that is there only
|
272
|
-
# in the new one
|
273
|
-
|
274
|
-
cor_options = scan.axis_params.get_nabu_cor_options_as_dict()
|
275
|
-
for key in "low_pass", "high_pass":
|
276
|
-
if key in cor_options:
|
277
|
-
cor_options[key] = int(cor_options[key])
|
278
|
-
corfinder = CompositeCOREstimator(
|
279
|
-
dataset_info=adapt_tomwer_scan_to_nabu(scan),
|
280
|
-
theta_interval=theta,
|
281
|
-
n_subsampling_y=n_subsampling_y,
|
282
|
-
oversampling=oversampling,
|
283
|
-
cor_options=cor_options,
|
284
|
-
logger=_logger,
|
285
|
-
take_log=take_log,
|
286
|
-
)
|
287
|
-
res = corfinder.find_cor()
|
288
|
-
return _absolute_pos_to_relative_with_warning(
|
289
|
-
absolute_pos=res, det_width=scan.dim_1
|
290
|
-
)
|
291
|
-
|
292
|
-
|
293
88
|
def get_composite_options(scan) -> tuple:
|
294
89
|
theta = scan.axis_params.composite_options.get("theta", DEFAULT_CMP_THETA)
|
295
90
|
n_subsampling_y = scan.axis_params.composite_options.get(
|
@@ -307,216 +102,6 @@ def get_composite_options(scan) -> tuple:
|
|
307
102
|
return theta, n_subsampling_y, oversampling, take_log, near_pos, near_width
|
308
103
|
|
309
104
|
|
310
|
-
def compute_scan_cor_nabu_growing_window(scan) -> float | None:
|
311
|
-
"""
|
312
|
-
Call to nabu.preproc.alignment.CenterOfRotation from the scan axis_params
|
313
|
-
value.
|
314
|
-
|
315
|
-
:param scan: scan to process
|
316
|
-
"""
|
317
|
-
if not scan.axis_params.use_sinogram:
|
318
|
-
radio_1, radio_2 = AxisTask.get_inputs(scan=scan)
|
319
|
-
if radio_1 is None or radio_2 is None:
|
320
|
-
raise NoAxisUrl("Unable to find projections for nabu axis calculation")
|
321
|
-
else:
|
322
|
-
radio_1, radio_2 = None, None
|
323
|
-
|
324
|
-
_logger.info(
|
325
|
-
"compute scan axis from nabu CenterOfRotationGrowingWindow with padding "
|
326
|
-
"mode {} and side {}. Use sinogram: {}".format(
|
327
|
-
scan.axis_params.padding_mode,
|
328
|
-
scan.axis_params.side,
|
329
|
-
scan.axis_params.use_sinogram,
|
330
|
-
)
|
331
|
-
)
|
332
|
-
|
333
|
-
if scan.axis_params.use_sinogram:
|
334
|
-
return compute_cor_nabu_growing_window_sinogram(scan=scan)
|
335
|
-
else:
|
336
|
-
return compute_cor_nabu_growing_window_radios(scan=scan)
|
337
|
-
|
338
|
-
|
339
|
-
def compute_cor_nabu_sliding_window(
|
340
|
-
radio_1: numpy.ndarray,
|
341
|
-
radio_2: numpy.ndarray,
|
342
|
-
side: str,
|
343
|
-
padding_mode,
|
344
|
-
flip_frame_2_lr=True,
|
345
|
-
horz_fft_width=False,
|
346
|
-
) -> float | None:
|
347
|
-
"""
|
348
|
-
Call nabu.preproc.alignement.CenterOfRotationSlidingWindow.find_shift
|
349
|
-
|
350
|
-
:param radio_1:
|
351
|
-
:param radio_2:
|
352
|
-
:param padding_mode:
|
353
|
-
:param side: side of the cor
|
354
|
-
:param horz_fft_width:
|
355
|
-
:param flip_frame_2_lr: if True will left-right flip the second frame
|
356
|
-
:param half_acq_cor_guess: The approximate position of the rotation axis
|
357
|
-
from the image center. Optional. When given a
|
358
|
-
special algorithm is used which can work also
|
359
|
-
in half-tomo conditions.
|
360
|
-
"""
|
361
|
-
nabu_class = CenterOfRotationSlidingWindow(horz_fft_width=horz_fft_width)
|
362
|
-
res = nabu_class.find_shift(
|
363
|
-
img_1=radio_1,
|
364
|
-
img_2=numpy.fliplr(radio_2) if flip_frame_2_lr else radio_2,
|
365
|
-
side=side,
|
366
|
-
roi_yxhw=None,
|
367
|
-
median_filt_shape=None,
|
368
|
-
)
|
369
|
-
return res
|
370
|
-
|
371
|
-
|
372
|
-
def compute_cor_nabu_sliding_window_radios(
|
373
|
-
scan: TomwerScanBase,
|
374
|
-
) -> float | None:
|
375
|
-
"""
|
376
|
-
Call nabu.preproc.alignment.CenterOfRotationGrowingWindow.find_shift
|
377
|
-
|
378
|
-
:param TomwerScanBase scan:
|
379
|
-
"""
|
380
|
-
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
381
|
-
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
382
|
-
|
383
|
-
projection_angles = scan.get_proj_angle_url()
|
384
|
-
projection_angles_i = {
|
385
|
-
value.path(): key for key, value in projection_angles.items()
|
386
|
-
}
|
387
|
-
url_radio_1, url_radio_2 = AxisTask.get_inputs_urls(scan=scan)
|
388
|
-
angle_radio_1 = float(projection_angles_i[url_radio_1.url.path()])
|
389
|
-
angle_radio_2 = float(projection_angles_i[url_radio_2.url.path()])
|
390
|
-
radio_angles = tuple(numpy.radians((angle_radio_1, angle_radio_2)))
|
391
|
-
|
392
|
-
corfinder = CORFinder(
|
393
|
-
dataset_info=adapt_tomwer_scan_to_nabu(scan),
|
394
|
-
method="sliding-window",
|
395
|
-
do_flatfield=has_darks and has_flats,
|
396
|
-
cor_options=scan.axis_params.get_nabu_cor_options_as_dict(),
|
397
|
-
radio_angles=radio_angles,
|
398
|
-
logger=_logger,
|
399
|
-
)
|
400
|
-
res = corfinder.find_cor() # Returns absolute cor.
|
401
|
-
return _absolute_pos_to_relative_with_warning(
|
402
|
-
absolute_pos=res, det_width=scan.dim_1
|
403
|
-
)
|
404
|
-
|
405
|
-
|
406
|
-
def compute_cor_nabu_sliding_window_sinogram(
|
407
|
-
scan: TomwerScanBase,
|
408
|
-
) -> float | None:
|
409
|
-
"""
|
410
|
-
Call nabu.preproc.alignment.CenterOfRotationGrowingWindow.find_shift
|
411
|
-
|
412
|
-
:param TomwerScanBase scan:
|
413
|
-
"""
|
414
|
-
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
415
|
-
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
416
|
-
|
417
|
-
corfinder = SinoCORFinder(
|
418
|
-
dataset_info=adapt_tomwer_scan_to_nabu(scan),
|
419
|
-
method="sino-sliding-window",
|
420
|
-
slice_idx=scan.axis_params.sinogram_line or "middle",
|
421
|
-
subsampling=scan.axis_params.sinogram_subsampling,
|
422
|
-
do_flatfield=has_darks and has_flats,
|
423
|
-
cor_options=scan.axis_params.get_nabu_cor_options_as_dict(),
|
424
|
-
logger=_logger,
|
425
|
-
)
|
426
|
-
res = corfinder.find_cor()
|
427
|
-
return _absolute_pos_to_relative_with_warning(
|
428
|
-
absolute_pos=res, det_width=scan.dim_1
|
429
|
-
)
|
430
|
-
|
431
|
-
|
432
|
-
def compute_scan_cor_nabu_sliding_window(scan: TomwerScanBase) -> float | None:
|
433
|
-
"""
|
434
|
-
Call to nabu.preproc.alignment.CenterOfRotation from the scan axis_params
|
435
|
-
value.
|
436
|
-
|
437
|
-
:param scan: scan to process
|
438
|
-
"""
|
439
|
-
if not scan.axis_params.use_sinogram:
|
440
|
-
radio_1, radio_2 = AxisTask.get_inputs(scan=scan)
|
441
|
-
if radio_1 is None or radio_2 is None:
|
442
|
-
raise NoAxisUrl("Unable to find projections for nabu axis calculation")
|
443
|
-
else:
|
444
|
-
radio_1 = radio_2 = None
|
445
|
-
|
446
|
-
_logger.info(
|
447
|
-
"compute scan axis from nabu CenterOfRotationSlidingWindow with padding "
|
448
|
-
"mode {} and side {}. Use sinogram: {}".format(
|
449
|
-
scan.axis_params.padding_mode,
|
450
|
-
scan.axis_params.side,
|
451
|
-
scan.axis_params.use_sinogram,
|
452
|
-
)
|
453
|
-
)
|
454
|
-
|
455
|
-
if scan.axis_params.use_sinogram:
|
456
|
-
return compute_cor_nabu_sliding_window_sinogram(scan=scan)
|
457
|
-
else:
|
458
|
-
return compute_cor_nabu_sliding_window_radios(scan=scan)
|
459
|
-
|
460
|
-
|
461
|
-
def compute_scan_fourier_angles(scan: TomwerScanBase) -> float | None:
|
462
|
-
"""
|
463
|
-
run 'scan_fourier_angles' algorithm for the requested scan
|
464
|
-
"""
|
465
|
-
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
466
|
-
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
467
|
-
|
468
|
-
corfinder = SinoCORFinder(
|
469
|
-
dataset_info=adapt_tomwer_scan_to_nabu(scan),
|
470
|
-
method="fourier-angles",
|
471
|
-
slice_idx=scan.axis_params.sinogram_line or "middle",
|
472
|
-
subsampling=scan.axis_params.sinogram_subsampling,
|
473
|
-
do_flatfield=has_darks and has_flats,
|
474
|
-
cor_options=scan.axis_params.get_nabu_cor_options_as_dict(),
|
475
|
-
logger=_logger,
|
476
|
-
)
|
477
|
-
res = corfinder.find_cor()
|
478
|
-
return _absolute_pos_to_relative_with_warning(
|
479
|
-
absolute_pos=res, det_width=scan.dim_1
|
480
|
-
)
|
481
|
-
|
482
|
-
|
483
|
-
def compute_scan_octave_accurate_radios(
|
484
|
-
scan: TomwerScanBase,
|
485
|
-
) -> float | None:
|
486
|
-
"""
|
487
|
-
Call nabu.preproc.alignment.CenterOfRotationGrowingWindow.find_shift
|
488
|
-
|
489
|
-
:return:
|
490
|
-
"""
|
491
|
-
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
492
|
-
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
493
|
-
|
494
|
-
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
495
|
-
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
496
|
-
|
497
|
-
projection_angles = scan.get_proj_angle_url()
|
498
|
-
projection_angles_i = {
|
499
|
-
value.path(): key for key, value in projection_angles.items()
|
500
|
-
}
|
501
|
-
url_radio_1, url_radio_2 = AxisTask.get_inputs_urls(scan=scan)
|
502
|
-
angle_radio_1 = float(projection_angles_i[url_radio_1.url.path()])
|
503
|
-
angle_radio_2 = float(projection_angles_i[url_radio_2.url.path()])
|
504
|
-
radio_angles = tuple(numpy.radians((angle_radio_1, angle_radio_2)))
|
505
|
-
|
506
|
-
corfinder = CORFinder(
|
507
|
-
dataset_info=adapt_tomwer_scan_to_nabu(scan),
|
508
|
-
method="octave-accurate",
|
509
|
-
do_flatfield=has_darks and has_flats,
|
510
|
-
cor_options=scan.axis_params.get_nabu_cor_options_as_dict(),
|
511
|
-
radio_angles=radio_angles,
|
512
|
-
logger=_logger,
|
513
|
-
)
|
514
|
-
res = corfinder.find_cor()
|
515
|
-
return _absolute_pos_to_relative_with_warning(
|
516
|
-
absolute_pos=res, det_width=scan.dim_1
|
517
|
-
)
|
518
|
-
|
519
|
-
|
520
105
|
def read_scan_x_rotation_axis_pixel_position(scan: TomwerScanBase):
|
521
106
|
"""read center of rotation estimation from metadata"""
|
522
107
|
if isinstance(scan, EDFTomoScan):
|
@@ -530,214 +115,6 @@ def read_scan_x_rotation_axis_pixel_position(scan: TomwerScanBase):
|
|
530
115
|
return res
|
531
116
|
|
532
117
|
|
533
|
-
def compute_scan_octave_accurate(scan: TomwerScanBase) -> float | None:
|
534
|
-
"""
|
535
|
-
Compute center of rotation from `octave-accurate` algorithm
|
536
|
-
scan
|
537
|
-
"""
|
538
|
-
cor_options = scan.axis_params.get_nabu_cor_options_as_dict()
|
539
|
-
radio_1, radio_2 = AxisTask.get_inputs(scan=scan)
|
540
|
-
extra_options = {}
|
541
|
-
for key in "low_pass", "high_pass":
|
542
|
-
if key in cor_options:
|
543
|
-
extra_options[key] = float(cor_options[key])
|
544
|
-
|
545
|
-
corfinder = CenterOfRotationOctaveAccurate()
|
546
|
-
res = corfinder.find_shift(
|
547
|
-
img_1=radio_1,
|
548
|
-
img_2=numpy.fliplr(radio_2) if scan.axis_params.flip_lr else radio_2,
|
549
|
-
side=scan.axis_params.side,
|
550
|
-
padding_mode=scan.axis_params.padding_mode,
|
551
|
-
**extra_options,
|
552
|
-
)
|
553
|
-
return res
|
554
|
-
|
555
|
-
|
556
|
-
def compute_cor_nabu_centered_radios(
|
557
|
-
scan: TomwerScanBase,
|
558
|
-
) -> float | None:
|
559
|
-
"""
|
560
|
-
Call nabu.preproc.alignment.CenterOfRotationGrowingWindow.find_shift
|
561
|
-
"""
|
562
|
-
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
563
|
-
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
564
|
-
|
565
|
-
projection_angles = scan.get_proj_angle_url()
|
566
|
-
projection_angles_i = {
|
567
|
-
value.path(): key for key, value in projection_angles.items()
|
568
|
-
}
|
569
|
-
url_radio_1, url_radio_2 = AxisTask.get_inputs_urls(scan=scan)
|
570
|
-
angle_radio_1 = float(projection_angles_i[url_radio_1.url.path()])
|
571
|
-
angle_radio_2 = float(projection_angles_i[url_radio_2.url.path()])
|
572
|
-
radio_angles = tuple(numpy.radians((angle_radio_1, angle_radio_2)))
|
573
|
-
|
574
|
-
corfinder = CORFinder(
|
575
|
-
dataset_info=adapt_tomwer_scan_to_nabu(scan),
|
576
|
-
method="centered",
|
577
|
-
do_flatfield=has_darks and has_flats,
|
578
|
-
cor_options=scan.axis_params.get_nabu_cor_options_as_dict(),
|
579
|
-
radio_angles=radio_angles,
|
580
|
-
logger=_logger,
|
581
|
-
)
|
582
|
-
res = corfinder.find_cor()
|
583
|
-
return _absolute_pos_to_relative_with_warning(
|
584
|
-
absolute_pos=res, det_width=scan.dim_1
|
585
|
-
)
|
586
|
-
|
587
|
-
|
588
|
-
def compute_cor_nabu_centered(
|
589
|
-
radio_1: numpy.ndarray,
|
590
|
-
radio_2: numpy.ndarray,
|
591
|
-
padding_mode,
|
592
|
-
flip_frame_2_lr=True,
|
593
|
-
horz_fft_width=False,
|
594
|
-
vert_fft_width=False,
|
595
|
-
):
|
596
|
-
"""
|
597
|
-
Call nabu.preproc.alignement.CenterOfRotation.find_shift
|
598
|
-
|
599
|
-
:param radio_1:
|
600
|
-
:param radio_2:
|
601
|
-
:param padding_mode:
|
602
|
-
:param horz_fft_width:
|
603
|
-
:param flip_frame_2_lr: if True will left-right flip the second frame
|
604
|
-
:param half_acq_cor_guess: The approximate position of the rotation axis
|
605
|
-
from the image center. Optional. When given a
|
606
|
-
special algorithm is used which can work also
|
607
|
-
in half-tomo conditions.
|
608
|
-
|
609
|
-
:return:
|
610
|
-
"""
|
611
|
-
nabu_class = CenterOfRotation(
|
612
|
-
horz_fft_width=horz_fft_width, vert_fft_width=vert_fft_width
|
613
|
-
)
|
614
|
-
return nabu_class.find_shift(
|
615
|
-
img_1=radio_1,
|
616
|
-
img_2=numpy.fliplr(radio_2) if flip_frame_2_lr else radio_2,
|
617
|
-
roi_yxhw=None,
|
618
|
-
padding_mode=padding_mode,
|
619
|
-
median_filt_shape=None,
|
620
|
-
)
|
621
|
-
|
622
|
-
|
623
|
-
def compute_scan_cor_nabu_centered(scan: TomwerScanBase) -> float | None:
|
624
|
-
"""
|
625
|
-
Call to nabu.preproc.alignment.CenterOfRotation from the scan axis_params
|
626
|
-
value.
|
627
|
-
|
628
|
-
:param scan: scan to process
|
629
|
-
"""
|
630
|
-
assert scan.axis_params is not None
|
631
|
-
radio_1, radio_2 = AxisTask.get_inputs(scan=scan)
|
632
|
-
if radio_1 is None or radio_2 is None:
|
633
|
-
raise NoAxisUrl("Unable to find projections for nabu axis calculation")
|
634
|
-
|
635
|
-
_logger.info(
|
636
|
-
"compute scan axis from nabu CenterOfRotation with padding "
|
637
|
-
"mode %s" % scan.axis_params.padding_mode
|
638
|
-
)
|
639
|
-
|
640
|
-
return compute_cor_nabu_centered_radios(scan)
|
641
|
-
|
642
|
-
|
643
|
-
def compute_cor_nabu_global(
|
644
|
-
radio_1: numpy.ndarray,
|
645
|
-
radio_2: numpy.ndarray,
|
646
|
-
padding_mode,
|
647
|
-
flip_frame_2_lr=True,
|
648
|
-
horz_fft_width=False,
|
649
|
-
):
|
650
|
-
"""
|
651
|
-
Call nabu.preproc.alignement.CenterOfRotation.find_shift
|
652
|
-
|
653
|
-
:param radio_1:
|
654
|
-
:param radio_2:
|
655
|
-
:param padding_mode:
|
656
|
-
:param horz_fft_width:
|
657
|
-
:param flip_frame_2_lr: if True will left-right flip the second frame
|
658
|
-
:return:
|
659
|
-
"""
|
660
|
-
nabu_class = CenterOfRotationAdaptiveSearch(horz_fft_width=horz_fft_width)
|
661
|
-
return nabu_class.find_shift(
|
662
|
-
img_1=radio_1,
|
663
|
-
img_2=numpy.fliplr(radio_2) if flip_frame_2_lr else radio_2,
|
664
|
-
roi_yxhw=None,
|
665
|
-
padding_mode=padding_mode,
|
666
|
-
median_filt_shape=None,
|
667
|
-
)
|
668
|
-
|
669
|
-
|
670
|
-
def compute_cor_nabu_global_radios(
|
671
|
-
scan: TomwerScanBase,
|
672
|
-
) -> float | None:
|
673
|
-
"""
|
674
|
-
Call nabu.preproc.alignement.CenterOfRotationGrowingWindow.find_shift
|
675
|
-
|
676
|
-
:param scan:
|
677
|
-
|
678
|
-
:return:
|
679
|
-
"""
|
680
|
-
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
681
|
-
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
682
|
-
|
683
|
-
projection_angles = scan.get_proj_angle_url()
|
684
|
-
projection_angles_i = {
|
685
|
-
value.path(): key for key, value in projection_angles.items()
|
686
|
-
}
|
687
|
-
url_radio_1, url_radio_2 = AxisTask.get_inputs_urls(scan=scan)
|
688
|
-
angle_radio_1 = float(projection_angles_i[url_radio_1.url.path()])
|
689
|
-
angle_radio_2 = float(projection_angles_i[url_radio_2.url.path()])
|
690
|
-
radio_angles = tuple(numpy.radians((angle_radio_1, angle_radio_2)))
|
691
|
-
|
692
|
-
corfinder = CORFinder(
|
693
|
-
dataset_info=adapt_tomwer_scan_to_nabu(scan),
|
694
|
-
method="global",
|
695
|
-
do_flatfield=has_darks and has_flats,
|
696
|
-
cor_options=scan.axis_params.get_nabu_cor_options_as_dict(),
|
697
|
-
radio_angles=radio_angles,
|
698
|
-
logger=_logger,
|
699
|
-
)
|
700
|
-
res = corfinder.find_cor()
|
701
|
-
return _absolute_pos_to_relative_with_warning(
|
702
|
-
absolute_pos=res, det_width=scan.dim_1
|
703
|
-
)
|
704
|
-
|
705
|
-
|
706
|
-
def compute_scan_cor_nabu_global(scan: TomwerScanBase) -> float | None:
|
707
|
-
"""
|
708
|
-
Call to nabu.preproc.alignment.CenterOfRotation from the scan axis_params
|
709
|
-
value.
|
710
|
-
|
711
|
-
:param scan: scan to process
|
712
|
-
"""
|
713
|
-
assert scan.axis_params is not None
|
714
|
-
radio_1, radio_2 = AxisTask.get_inputs(scan=scan)
|
715
|
-
if radio_1 is None or radio_2 is None:
|
716
|
-
raise NoAxisUrl("Unable to find projections for nabu axis calculation")
|
717
|
-
|
718
|
-
_logger.info(
|
719
|
-
"compute scan axis from nabu CenterOfRotation with padding "
|
720
|
-
"mode %s" % scan.axis_params.padding_mode
|
721
|
-
)
|
722
|
-
return compute_cor_nabu_global_radios(scan)
|
723
|
-
|
724
|
-
|
725
|
-
def get_stdmax_column(x: numpy.ndarray) -> float:
|
726
|
-
"""
|
727
|
-
|
728
|
-
:return: column index of the maximal standard deviation
|
729
|
-
"""
|
730
|
-
kernel_size = 5
|
731
|
-
length = len(x)
|
732
|
-
r = range(length - kernel_size)
|
733
|
-
y = numpy.empty(length - kernel_size)
|
734
|
-
for i in r:
|
735
|
-
s = numpy.std(x[i : i + kernel_size])
|
736
|
-
y[i] = s
|
737
|
-
|
738
|
-
return y.argmax()
|
739
|
-
|
740
|
-
|
741
118
|
class NoAxisUrl(Exception):
|
742
119
|
pass
|
743
120
|
|
@@ -753,21 +130,6 @@ class AxisTask(
|
|
753
130
|
Process used to compute the center of rotation of a scan
|
754
131
|
"""
|
755
132
|
|
756
|
-
_CALCULATIONS_METHODS = {
|
757
|
-
AxisMode.centered: compute_scan_cor_nabu_centered,
|
758
|
-
AxisMode.global_: compute_scan_cor_nabu_global,
|
759
|
-
AxisMode.sliding_window_sinogram: compute_scan_cor_nabu_sliding_window,
|
760
|
-
AxisMode.sliding_window_radios: compute_scan_cor_nabu_sliding_window,
|
761
|
-
AxisMode.growing_window_sinogram: compute_scan_cor_nabu_growing_window,
|
762
|
-
AxisMode.growing_window_radios: compute_scan_cor_nabu_growing_window,
|
763
|
-
AxisMode.sino_coarse_to_fine: compute_scan_sino_coarse_to_fine,
|
764
|
-
AxisMode.composite_coarse_to_fine: compute_scan_composite_coarse_to_fine,
|
765
|
-
AxisMode.near: compute_scan_composite_coarse_to_fine,
|
766
|
-
AxisMode.sino_fourier_angles: compute_scan_fourier_angles,
|
767
|
-
AxisMode.octave_accurate_radios: compute_scan_octave_accurate,
|
768
|
-
AxisMode.read: read_scan_x_rotation_axis_pixel_position,
|
769
|
-
}
|
770
|
-
|
771
133
|
def __init__(
|
772
134
|
self,
|
773
135
|
process_id=None,
|
@@ -942,12 +304,12 @@ class AxisTask(
|
|
942
304
|
_logger.error(e)
|
943
305
|
|
944
306
|
@staticmethod
|
945
|
-
def
|
307
|
+
def get_input_radios(scan) -> tuple:
|
946
308
|
"""Make sure we have valid projections to be used for axis calculation
|
947
309
|
|
948
310
|
:param scan: scan to check
|
949
311
|
:raise: NoAxisUrl if fails to found
|
950
|
-
:return:
|
312
|
+
:return: tuple of AxisResource
|
951
313
|
"""
|
952
314
|
if (
|
953
315
|
scan.axis_params
|
@@ -955,16 +317,13 @@ class AxisTask(
|
|
955
317
|
and scan.axis_params.axis_url_1.url
|
956
318
|
):
|
957
319
|
return scan.axis_params.axis_url_1, scan.axis_params.axis_url_2
|
958
|
-
|
959
|
-
|
960
|
-
mode=scan.axis_params.angle_mode
|
961
|
-
)
|
962
|
-
return _radio_1, _radio_2
|
320
|
+
|
321
|
+
return scan.get_opposite_projections(mode=scan.axis_params.angle_mode)
|
963
322
|
|
964
323
|
@staticmethod
|
965
324
|
def get_inputs(scan):
|
966
325
|
assert isinstance(scan, TomwerScanBase)
|
967
|
-
radio_1, radio_2 = AxisTask.
|
326
|
+
radio_1, radio_2 = AxisTask.get_input_radios(scan=scan)
|
968
327
|
if radio_1 and radio_2:
|
969
328
|
mess = " ".join(
|
970
329
|
("input radios are", radio_1.url.path(), "and", radio_2.url.path())
|
@@ -1047,14 +406,8 @@ class AxisTask(
|
|
1047
406
|
self._axis_params.frame_width = scan.dim_1
|
1048
407
|
self._axis_params.set_relative_value(position)
|
1049
408
|
scan_name = scan.path or "undef scan"
|
1050
|
-
if scan.axis_params.use_sinogram:
|
1051
|
-
method = "sinogram"
|
1052
|
-
else:
|
1053
|
-
method = scan.axis_params.mode.value
|
1054
409
|
r_cor_value = scan.axis_params.relative_cor_value
|
1055
|
-
mess = (
|
1056
|
-
f"Compute axis position ({r_cor_value}) with {method} for {scan_name}"
|
1057
|
-
)
|
410
|
+
mess = f"Compute axis position ({r_cor_value}) with {scan.axis_params.mode.value}"
|
1058
411
|
_logger.info(mess)
|
1059
412
|
return scan
|
1060
413
|
|
@@ -1077,18 +430,30 @@ class AxisTask(
|
|
1077
430
|
|
1078
431
|
:param scan: scan for which we compute the center of rotation
|
1079
432
|
:return: position of the rotation axis. Use the `.AxisMode` defined
|
1080
|
-
by the `.ReconsParams` of the `.
|
433
|
+
by the `.ReconsParams` of the `.AxisTask`
|
1081
434
|
"""
|
1082
435
|
mode = self._axis_params.mode
|
1083
|
-
if mode
|
436
|
+
if mode is AxisMode.manual:
|
1084
437
|
# If mode is read or manual the position_value is not computed and
|
1085
438
|
# we will keep the actual one (should have been defined previously)
|
1086
439
|
res = self._axis_params.relative_cor_value
|
1087
|
-
elif mode
|
1088
|
-
|
1089
|
-
res = self._CALCULATIONS_METHODS[mode](scan)
|
440
|
+
elif mode is AxisMode.read:
|
441
|
+
res = read_scan_x_rotation_axis_pixel_position(scan=scan)
|
1090
442
|
else:
|
1091
|
-
|
443
|
+
has_darks = scan.reduced_darks is not None and len(scan.reduced_darks) > 0
|
444
|
+
has_flats = scan.reduced_flats is not None and len(scan.reduced_flats) > 0
|
445
|
+
|
446
|
+
res = estimate_cor(
|
447
|
+
method=mode.value,
|
448
|
+
dataset_info=adapt_tomwer_scan_to_nabu(scan=scan),
|
449
|
+
do_flatfield=has_darks and has_flats,
|
450
|
+
cor_options=scan.axis_params.get_nabu_cor_options_as_dict(),
|
451
|
+
)
|
452
|
+
# convert back to relative
|
453
|
+
res = _absolute_pos_to_relative_with_warning(
|
454
|
+
absolute_pos=res, det_width=scan.dim_1
|
455
|
+
)
|
456
|
+
|
1092
457
|
return res
|
1093
458
|
|
1094
459
|
@docstring(Task.program_name)
|
@@ -1105,77 +470,3 @@ class AxisTask(
|
|
1105
470
|
@staticmethod
|
1106
471
|
def definition():
|
1107
472
|
return "Compute center of rotation"
|
1108
|
-
|
1109
|
-
@staticmethod
|
1110
|
-
def get_cor_frm_process_file(process_file, entry, as_url=False) -> float | None:
|
1111
|
-
"""
|
1112
|
-
Read cor position from a tomwer_process file
|
1113
|
-
|
1114
|
-
:param process_file:
|
1115
|
-
:param entry:
|
1116
|
-
:return:
|
1117
|
-
"""
|
1118
|
-
if entry is None:
|
1119
|
-
with open_hdf5(process_file) as h5f:
|
1120
|
-
entries = AxisTask._get_process_nodes(root_node=h5f, process=AxisTask)
|
1121
|
-
if len(entries) == 0:
|
1122
|
-
_logger.info("unable to find a Axis process in %s" % process_file)
|
1123
|
-
return None
|
1124
|
-
elif len(entries) > 1:
|
1125
|
-
raise ValueError("several entry found, entry should be " "specify")
|
1126
|
-
else:
|
1127
|
-
entry = list(entries.keys())[0]
|
1128
|
-
_logger.info("take %s as default entry" % entry)
|
1129
|
-
|
1130
|
-
with open_hdf5(process_file) as h5f:
|
1131
|
-
axis_nodes = AxisTask._get_process_nodes(
|
1132
|
-
root_node=h5f[entry], process=AxisTask
|
1133
|
-
)
|
1134
|
-
index_to_path = {}
|
1135
|
-
for key, index in axis_nodes.items():
|
1136
|
-
index_to_path[index] = key
|
1137
|
-
|
1138
|
-
if len(axis_nodes) == 0:
|
1139
|
-
return None
|
1140
|
-
# take the last processed dark ref
|
1141
|
-
last_process_index = sorted(list(axis_nodes.values()))[-1]
|
1142
|
-
last_process_dark = index_to_path[last_process_index]
|
1143
|
-
if (len(index_to_path)) > 1:
|
1144
|
-
_logger.debug(
|
1145
|
-
"several processing found for dark-ref,"
|
1146
|
-
"take the last one: %s" % last_process_dark
|
1147
|
-
)
|
1148
|
-
|
1149
|
-
res = None
|
1150
|
-
if "results" in h5f[last_process_dark].keys():
|
1151
|
-
results_node = h5f[last_process_dark]["results"]
|
1152
|
-
if "center_of_rotation" in results_node.keys():
|
1153
|
-
if as_url:
|
1154
|
-
res = DataUrl(
|
1155
|
-
file_path=process_file,
|
1156
|
-
data_path="/".join((results_node, "center_of_rotation")),
|
1157
|
-
scheme="h5py",
|
1158
|
-
)
|
1159
|
-
else:
|
1160
|
-
res = h5py_read_dataset(results_node["center_of_rotation"])
|
1161
|
-
return res
|
1162
|
-
|
1163
|
-
|
1164
|
-
class AxisProcess(AxisTask):
|
1165
|
-
def __init__(
|
1166
|
-
self,
|
1167
|
-
process_id=None,
|
1168
|
-
varinfo=None,
|
1169
|
-
inputs=None,
|
1170
|
-
node_id=None,
|
1171
|
-
node_attrs=None,
|
1172
|
-
execinfo=None,
|
1173
|
-
):
|
1174
|
-
deprecated_warning(
|
1175
|
-
name="tomwer.core.process.reconstruction.axis.axis.AxisProcess",
|
1176
|
-
type_="class",
|
1177
|
-
reason="improve readibility",
|
1178
|
-
since_version="1.2",
|
1179
|
-
replacement="AxisTask",
|
1180
|
-
)
|
1181
|
-
super().__init__(process_id, varinfo, inputs, node_id, node_attrs, execinfo)
|