tomwer 1.4.0rc0__py3-none-any.whl → 1.4.0rc2__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/nxtomoscan.py +2 -0
- 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 +394 -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.0rc2.dist-info}/METADATA +2 -2
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/RECORD +51 -48
- tomwer/core/process/tests/test_axis.py +0 -231
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/LICENSE +0 -0
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/WHEEL +0 -0
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/entry_points.txt +0 -0
- {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/top_level.txt +0 -0
@@ -5,10 +5,11 @@ from collections import namedtuple
|
|
5
5
|
|
6
6
|
import numpy
|
7
7
|
from silx.io.url import DataUrl
|
8
|
-
from silx.utils.deprecation import deprecated
|
9
8
|
from silx.utils.enum import Enum as _Enum
|
10
9
|
from tomoscan.esrf.scan.utils import get_data
|
11
10
|
|
11
|
+
from tomwer.core.process.reconstruction.utils.cor import relative_pos_to_absolute
|
12
|
+
from tomwer.core.process.reconstruction.axis.side import Side
|
12
13
|
from tomwer.core.scan.scanbase import TomwerScanBase
|
13
14
|
|
14
15
|
from .anglemode import CorAngleMode
|
@@ -77,13 +78,14 @@ class AxisResource(object):
|
|
77
78
|
"""Paganin configuration for axis calculation. To simplify we have only one
|
78
79
|
static configuration for now. Otherwise complicate stuff"""
|
79
80
|
|
80
|
-
def __init__(self, url):
|
81
|
+
def __init__(self, url: None | DataUrl, angle: float | None = None):
|
81
82
|
assert url is None or isinstance(url, DataUrl)
|
82
83
|
assert url is None or url.is_valid()
|
83
84
|
self.__url = url
|
84
85
|
self.__raw_data = None
|
85
86
|
self.__norme_data = None
|
86
87
|
self.__norm_paganin = None
|
88
|
+
self.__angle = angle
|
87
89
|
|
88
90
|
def __str__(self):
|
89
91
|
return f"{type(self)}, url: {self.__url.path() if self.__url else None}"
|
@@ -116,6 +118,10 @@ class AxisResource(object):
|
|
116
118
|
def normalized_data(self, data):
|
117
119
|
self.__norme_data = data
|
118
120
|
|
121
|
+
@property
|
122
|
+
def angle(self) -> float | None:
|
123
|
+
return self.__angle
|
124
|
+
|
119
125
|
def normalize_data(self, scan, log_):
|
120
126
|
"""Normalize data for axis calculation"""
|
121
127
|
if not isinstance(scan, TomwerScanBase):
|
@@ -182,7 +188,7 @@ class AxisResource(object):
|
|
182
188
|
|
183
189
|
class AxisRP:
|
184
190
|
"""
|
185
|
-
Configuration class for a tomwer :class:`
|
191
|
+
Configuration class for a tomwer :class:`AxisTask`
|
186
192
|
|
187
193
|
note: every modification on the parameters will process a call fo changed
|
188
194
|
except `axis_url_1` and `axis_url_2` which will produce a call to the
|
@@ -206,11 +212,12 @@ class AxisRP:
|
|
206
212
|
"FINE_STEP_X",
|
207
213
|
"SCALE_IMG2_TO_IMG1",
|
208
214
|
"NEAR_POSITION",
|
215
|
+
"MOTOR_OFFSET",
|
216
|
+
"X_ROTATION_AXIS_PIXEL_POSITION",
|
209
217
|
"SINOGRAM_SUBSAMPLING",
|
210
218
|
"PADDING_MODE",
|
211
219
|
"FLIP_LR",
|
212
220
|
"COMPOSITE_OPTS",
|
213
|
-
"SIDE",
|
214
221
|
"COR_OPTIONS",
|
215
222
|
)
|
216
223
|
|
@@ -225,17 +232,18 @@ class AxisRP:
|
|
225
232
|
None is not processing"""
|
226
233
|
self.__angle_mode = CorAngleMode.use_0_180
|
227
234
|
"""Angle to use for radios"""
|
235
|
+
self.__x_rotation_axis_pixel_position = None
|
236
|
+
"""rotation axis position obtained from motor"""
|
237
|
+
self.__x_rotation_axis_pos_px_offset = 0.0
|
238
|
+
"""motor offset to be used to compute the 'estimated_cor' from 'x_rotation_axis_pixel_position'"""
|
228
239
|
self.__estimated_cor = 0.0
|
229
|
-
"""
|
240
|
+
"""Estimated position of the center of rotation. Given by the user or computed from 'x_rotation_axis_pixel_position' and 'pixel_offset'"""
|
230
241
|
self.__axis_url_1 = AxisResource(url=None)
|
231
242
|
"""first data url to use for axis cor calculation"""
|
232
243
|
self.__axis_url_2 = AxisResource(url=None)
|
233
244
|
"""second data url to use for axis cor calculation"""
|
234
245
|
self.__calculation_input_type = AxisCalculationInput.transmission
|
235
246
|
"""Type of input (emission, absorption, with or without paganin)"""
|
236
|
-
self.__use_sinogram = False
|
237
|
-
"""Do we want to use the sinogram of the radios for computing center
|
238
|
-
of rotation"""
|
239
247
|
self.__sinogram_line = "middle"
|
240
248
|
"""Line of the radios to use for getting the sinogram"""
|
241
249
|
self.__sinogram_subsampling = 10
|
@@ -244,17 +252,14 @@ class AxisRP:
|
|
244
252
|
self.__look_at_stdmax = False
|
245
253
|
"""do the near search at X position which as the max Y column standard
|
246
254
|
deviation"""
|
247
|
-
self.
|
255
|
+
self.__composite_window_size = 5
|
248
256
|
"""do the near search in an X window of size +-near_wx"""
|
249
|
-
self.
|
257
|
+
self.__composite_fine_step = 0.1
|
250
258
|
"""shift step x for fine shifting image"""
|
251
259
|
self.__scale_img2_to_img1 = False
|
252
260
|
"""do image scaling"""
|
253
261
|
self.__padding_mode = None
|
254
262
|
self.__frame_width = None
|
255
|
-
self.__side = "right"
|
256
|
-
"""side of the cor. Requested by nabu cor algorithms growing-window
|
257
|
-
and sliding-window"""
|
258
263
|
self.__flip_lr = True
|
259
264
|
self.__composite_options = {
|
260
265
|
"theta": DEFAULT_CMP_THETA,
|
@@ -274,37 +279,7 @@ class AxisRP:
|
|
274
279
|
|
275
280
|
@mode.setter
|
276
281
|
def mode(self, mode):
|
277
|
-
|
278
|
-
try:
|
279
|
-
name = mode
|
280
|
-
if name in ("global_", "global"):
|
281
|
-
name = AxisMode.global_.name
|
282
|
-
elif name == "accurate":
|
283
|
-
_logger.info(
|
284
|
-
f"convert axis mode {name} to {AxisMode.centered.name} (renamed)"
|
285
|
-
)
|
286
|
-
name = AxisMode.centered.name
|
287
|
-
elif name == "growing-window":
|
288
|
-
_logger.warning(
|
289
|
-
f"growing-window mode has been removed. Replace it by {AxisMode.growing_window_radios.value}"
|
290
|
-
)
|
291
|
-
name = AxisMode.growing_window_radios.name
|
292
|
-
elif name == "sliding-window":
|
293
|
-
_logger.warning(
|
294
|
-
f"sliding-window mode has been removed. Replace it by {AxisMode.sliding_window_radios.value}"
|
295
|
-
)
|
296
|
-
name = AxisMode.sliding_window_radios.name
|
297
|
-
try:
|
298
|
-
_mode = getattr(AxisMode, name)
|
299
|
-
except Exception:
|
300
|
-
_mode = AxisMode.from_value(name)
|
301
|
-
except Exception:
|
302
|
-
raise ValueError(f"Fail to create axis mode from {mode}")
|
303
|
-
else:
|
304
|
-
if not isinstance(mode, AxisMode):
|
305
|
-
raise TypeError(f"mode is expected to be an instance of {AxisMode}")
|
306
|
-
_mode = mode
|
307
|
-
self.__mode = _mode
|
282
|
+
self.__mode = AxisMode.from_value(mode)
|
308
283
|
self.changed()
|
309
284
|
|
310
285
|
@property
|
@@ -346,7 +321,7 @@ class AxisRP:
|
|
346
321
|
def set_relative_value(self, value):
|
347
322
|
if not isinstance(value, (int, float, str, type(None))):
|
348
323
|
raise TypeError(
|
349
|
-
f"value is expected to be an instance of {int} {float}, {str} or {None}. {type(value)} provided"
|
324
|
+
f"value is expected to be an instance of {int}, {float}, {str}, or {None}. {type(value)} provided"
|
350
325
|
)
|
351
326
|
if value is None:
|
352
327
|
changed = self.__relative_value is not None
|
@@ -361,32 +336,46 @@ class AxisRP:
|
|
361
336
|
else:
|
362
337
|
self.__relative_value = float(value)
|
363
338
|
if self.frame_width is not None:
|
364
|
-
self.__absolute_value = (
|
365
|
-
self.__relative_value
|
339
|
+
self.__absolute_value = relative_pos_to_absolute(
|
340
|
+
relative_pos=self.__relative_value, det_width=self.frame_width
|
366
341
|
)
|
367
342
|
self.changed()
|
368
343
|
|
369
344
|
@property
|
370
|
-
def estimated_cor(self):
|
345
|
+
def estimated_cor(self) -> Side | float | None:
|
371
346
|
return self.__estimated_cor
|
372
347
|
|
373
348
|
@estimated_cor.setter
|
374
|
-
def estimated_cor(self, value):
|
349
|
+
def estimated_cor(self, value: Side | float | None):
|
350
|
+
try:
|
351
|
+
value = Side.from_value(value)
|
352
|
+
except ValueError:
|
353
|
+
pass
|
375
354
|
if self.__estimated_cor != value:
|
376
355
|
self.__estimated_cor = value
|
377
356
|
self.changed()
|
378
357
|
|
379
358
|
@property
|
380
|
-
def
|
381
|
-
return self.
|
382
|
-
|
383
|
-
@
|
384
|
-
def
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
359
|
+
def x_rotation_axis_pos_px_offset(self) -> float:
|
360
|
+
return self.__x_rotation_axis_pos_px_offset
|
361
|
+
|
362
|
+
@x_rotation_axis_pos_px_offset.setter
|
363
|
+
def x_rotation_axis_pos_px_offset(self, value: float):
|
364
|
+
assert isinstance(
|
365
|
+
value, float
|
366
|
+
), f"x_rotation_axis_pos_px_offset should be a float. Got {type(value)}"
|
367
|
+
self.__x_rotation_axis_pos_px_offset = value
|
368
|
+
|
369
|
+
@property
|
370
|
+
def x_rotation_axis_pixel_position(self) -> float | None:
|
371
|
+
return self.__x_rotation_axis_pixel_position
|
372
|
+
|
373
|
+
@x_rotation_axis_pixel_position.setter
|
374
|
+
def x_rotation_axis_pixel_position(self, value: float | None):
|
375
|
+
assert isinstance(
|
376
|
+
value, (float, type(None))
|
377
|
+
), f"x_rotation_axis_pixel_position should be None or a float. Got{type(value)}"
|
378
|
+
self.__x_rotation_axis_pixel_position = value
|
390
379
|
|
391
380
|
@property
|
392
381
|
def axis_url_1(self):
|
@@ -470,16 +459,6 @@ class AxisRP:
|
|
470
459
|
self.__calculation_input_type = type_
|
471
460
|
self.changed()
|
472
461
|
|
473
|
-
@property
|
474
|
-
def use_sinogram(self):
|
475
|
-
return self.__use_sinogram
|
476
|
-
|
477
|
-
@use_sinogram.setter
|
478
|
-
def use_sinogram(self, sinogram):
|
479
|
-
if self.__use_sinogram != sinogram:
|
480
|
-
self.__use_sinogram = sinogram
|
481
|
-
self.changed()
|
482
|
-
|
483
462
|
@property
|
484
463
|
def sinogram_line(self):
|
485
464
|
return self.__sinogram_line
|
@@ -520,23 +499,24 @@ class AxisRP:
|
|
520
499
|
self.__look_at_stdmax = stdmax
|
521
500
|
|
522
501
|
@property
|
523
|
-
def
|
524
|
-
return self.
|
502
|
+
def composite_window_size(self):
|
503
|
+
return self.__composite_window_size
|
525
504
|
|
526
|
-
@
|
527
|
-
def
|
528
|
-
if self.
|
529
|
-
self.
|
505
|
+
@composite_window_size.setter
|
506
|
+
def composite_window_size(self, width):
|
507
|
+
if self.__composite_window_size != width:
|
508
|
+
self.__composite_window_size = width
|
530
509
|
self.changed()
|
531
510
|
|
532
511
|
@property
|
533
|
-
def
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
512
|
+
def composite_fine_step(self):
|
513
|
+
"""Fine step along x axis"""
|
514
|
+
return self.__composite_fine_step
|
515
|
+
|
516
|
+
@composite_fine_step.setter
|
517
|
+
def composite_fine_step(self, step_size):
|
518
|
+
if self.__composite_fine_step != step_size:
|
519
|
+
self.__composite_fine_step = step_size
|
540
520
|
self.changed()
|
541
521
|
|
542
522
|
@property
|
@@ -593,7 +573,7 @@ class AxisRP:
|
|
593
573
|
"near_pos",
|
594
574
|
"near_width",
|
595
575
|
):
|
596
|
-
raise KeyError(f"{key} is not
|
576
|
+
raise KeyError(f"{key} is not recognized")
|
597
577
|
self.__composite_options = opts
|
598
578
|
|
599
579
|
@property
|
@@ -607,7 +587,7 @@ class AxisRP:
|
|
607
587
|
self.changed()
|
608
588
|
|
609
589
|
def changed(self):
|
610
|
-
"""callback to overwrite when the
|
590
|
+
"""callback to overwrite when the parameter value changed"""
|
611
591
|
pass
|
612
592
|
|
613
593
|
def n_url(self):
|
@@ -640,21 +620,27 @@ class AxisRP:
|
|
640
620
|
"POSITION_VALUE": self.relative_cor_value,
|
641
621
|
"CALC_INPUT_TYPE": self.calculation_input_type.to_dict(),
|
642
622
|
"ANGLE_MODE": self.angle_mode.value,
|
643
|
-
"
|
644
|
-
|
623
|
+
"SINOGRAM_LINE": (
|
624
|
+
self.sinogram_line if self.mode.requires_sinogram_index() else ""
|
625
|
+
),
|
645
626
|
"SINOGRAM_SUBSAMPLING": self.sinogram_subsampling,
|
646
627
|
"AXIS_URL_1": axis_urls_1,
|
647
628
|
"AXIS_URL_2": axis_urls_2,
|
648
629
|
"LOOK_AT_STDMAX": self.look_at_stdmax,
|
649
|
-
"NEAR_WX": self.
|
650
|
-
"FINE_STEP_X": self.
|
630
|
+
"NEAR_WX": self.composite_window_size,
|
631
|
+
"FINE_STEP_X": self.composite_fine_step,
|
651
632
|
"SCALE_IMG2_TO_IMG1": self.scale_img2_to_img1,
|
652
|
-
"NEAR_POSITION":
|
633
|
+
"NEAR_POSITION": (
|
634
|
+
self.estimated_cor.value
|
635
|
+
if isinstance(self.estimated_cor, Side)
|
636
|
+
else self.estimated_cor
|
637
|
+
),
|
653
638
|
"PADDING_MODE": self.padding_mode,
|
654
639
|
"FLIP_LR": self.flip_lr,
|
655
640
|
"COMPOSITE_OPTS": self.composite_options,
|
656
|
-
"SIDE": self.side,
|
657
641
|
"COR_OPTIONS": self.extra_cor_options,
|
642
|
+
"MOTOR_OFFSET": self.x_rotation_axis_pos_px_offset,
|
643
|
+
"X_ROTATION_AXIS_PIXEL_POSITION": self.x_rotation_axis_pixel_position,
|
658
644
|
}
|
659
645
|
return _dict
|
660
646
|
|
@@ -679,8 +665,6 @@ class AxisRP:
|
|
679
665
|
self.calculation_input_type = AxisCalculationInput.from_value(
|
680
666
|
_dict["CALC_INPUT_TYPE"]
|
681
667
|
)
|
682
|
-
if "USE_SINOGRAM" in _dict:
|
683
|
-
self.use_sinogram = _dict["USE_SINOGRAM"]
|
684
668
|
if "ANGLE_MODE" in _dict:
|
685
669
|
self.angle_mode = CorAngleMode.from_value(_dict["ANGLE_MODE"])
|
686
670
|
if "SINOGRAM_LINE" in _dict:
|
@@ -692,13 +676,13 @@ class AxisRP:
|
|
692
676
|
if "LOOK_AT_STDMAX" in _dict:
|
693
677
|
self.look_at_stdmax = _dict["LOOK_AT_STDMAX"]
|
694
678
|
if "NEAR_WX" in _dict:
|
695
|
-
self.
|
679
|
+
self.composite_window_size = _dict["NEAR_WX"]
|
696
680
|
if "FINE_STEP_X" in _dict:
|
697
|
-
self.
|
681
|
+
self.composite_fine_step = _dict["FINE_STEP_X"]
|
698
682
|
if "SCALE_IMG2_TO_IMG1" in _dict:
|
699
683
|
self.scale_img2_to_img1 = _dict["SCALE_IMG2_TO_IMG1"]
|
700
684
|
if "NEAR_POSITION" in _dict:
|
701
|
-
self.estimated_cor =
|
685
|
+
self.estimated_cor = _dict["NEAR_POSITION"]
|
702
686
|
if "SINOGRAM_SUBSAMPLING" in _dict:
|
703
687
|
self.sinogram_subsampling = _dict["SINOGRAM_SUBSAMPLING"]
|
704
688
|
if "PADDING_MODE" in _dict:
|
@@ -707,8 +691,6 @@ class AxisRP:
|
|
707
691
|
self.flip_lr = bool(_dict["FLIP_LR"])
|
708
692
|
if "COMPOSITE_OPTS" in _dict:
|
709
693
|
self.composite_options = _dict["COMPOSITE_OPTS"]
|
710
|
-
if "SIDE" in _dict:
|
711
|
-
self.side = _dict["SIDE"]
|
712
694
|
self.extra_cor_options = _dict.get("COR_OPTIONS", "")
|
713
695
|
|
714
696
|
def copy(self, axis_params, copy_axis_url=True, copy_flip_lr=True):
|
@@ -717,17 +699,17 @@ class AxisRP:
|
|
717
699
|
self.frame_width = axis_params.frame_width
|
718
700
|
self.set_relative_value(axis_params.relative_cor_value)
|
719
701
|
self.calculation_input_type = axis_params.calculation_input_type
|
720
|
-
self.use_sinogram = axis_params.use_sinogram
|
721
702
|
self.angle_mode = axis_params.angle_mode
|
722
703
|
self.sinogram_line = axis_params.sinogram_line
|
723
704
|
self.sinogram_subsampling = axis_params.sinogram_subsampling
|
724
705
|
self.look_at_stdmax = axis_params.look_at_stdmax
|
725
|
-
self.
|
726
|
-
self.
|
706
|
+
self.composite_window_size = axis_params.composite_window_size
|
707
|
+
self.composite_fine_step = axis_params.composite_fine_step
|
727
708
|
self.scale_img2_to_img1 = axis_params.scale_img2_to_img1
|
728
709
|
self.estimated_cor = axis_params.estimated_cor
|
710
|
+
self.x_rotation_axis_pixel_position = axis_params.x_rotation_axis_pixel_position
|
711
|
+
self.x_rotation_axis_pos_px_offset = axis_params.x_rotation_axis_pos_px_offset
|
729
712
|
self.padding_mode = axis_params.padding_mode
|
730
|
-
self.side = axis_params.side
|
731
713
|
self.composite_options = axis_params.composite_options
|
732
714
|
self.extra_cor_options = axis_params.extra_cor_options
|
733
715
|
if copy_axis_url:
|
@@ -754,56 +736,63 @@ class AxisRP:
|
|
754
736
|
AxisMode.sliding_window_radios,
|
755
737
|
AxisMode.sliding_window_sinogram,
|
756
738
|
):
|
757
|
-
extra_info = f"side: {self.
|
739
|
+
extra_info = f"side: {self.estimated_cor}"
|
758
740
|
results = ", ".join((results, extra_info))
|
759
741
|
return results
|
760
742
|
|
761
743
|
def get_nabu_cor_options_as_dict(self) -> str:
|
762
744
|
options = {}
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
745
|
+
# provide side. Scalar if a first guess is provided else a value in ('left', 'right', 'center', 'all')
|
746
|
+
if (
|
747
|
+
isinstance(self.estimated_cor, Side)
|
748
|
+
and AXIS_MODE_METADATAS[self.mode].valid_sides
|
749
|
+
):
|
750
|
+
options["side"] = self.estimated_cor.value
|
751
|
+
elif AXIS_MODE_METADATAS[self.mode].allows_estimated_cor_as_numerical_value:
|
752
|
+
options["side"] = self.estimated_cor
|
753
|
+
|
754
|
+
if self.mode in (AxisMode.composite_coarse_to_fine, AxisMode.near):
|
755
|
+
options["near_width"] = self.composite_options.get(
|
756
|
+
"near_width", DEFAULT_CMP_NEAR_WIDTH
|
757
|
+
)
|
758
|
+
options["theta_interval"] = self.composite_options.get(
|
759
|
+
"theta_interval", DEFAULT_CMP_THETA
|
760
|
+
)
|
761
|
+
options["oversampling"] = self.composite_options.get(
|
762
|
+
"oversampling", DEFAULT_CMP_OVERSAMPLING
|
763
|
+
)
|
764
|
+
options["n_subsampling_y"] = self.composite_options.get(
|
765
|
+
"n_subsampling_y", DEFAULT_CMP_N_SUBSAMPLING_Y
|
766
|
+
)
|
767
|
+
options["take_log"] = self.composite_options.get(
|
768
|
+
"take_log", DEFAULT_CMP_TAKE_LOG
|
769
|
+
)
|
774
770
|
|
775
|
-
|
776
|
-
|
777
|
-
|
771
|
+
# provide radio indices or sinogram index
|
772
|
+
if self.mode.requires_radio_indices:
|
773
|
+
# warning: nabu expect radio angles to be in rad, in nxtomo and tomwer they are in degrees
|
774
|
+
options["radio_angles"] = (
|
775
|
+
numpy.deg2rad(self.axis_url_1.angle or 0.0),
|
776
|
+
numpy.deg2rad(self.axis_url_2.angle or 180.0),
|
777
|
+
)
|
778
|
+
if self.mode.requires_sinogram_index:
|
779
|
+
options["slice_idx"] = self.sinogram_line
|
778
780
|
|
779
781
|
# append "extra_cor_options" to already handled cor options
|
782
|
+
# expected values: low_pass, high_pass
|
780
783
|
extra_cor_options = self.extra_cor_options.replace(" ", "")
|
781
784
|
|
782
785
|
if extra_cor_options != "":
|
783
786
|
for opt in self.extra_cor_options.replace(" ", "").split(";"):
|
784
787
|
if len(opt.split("=")) == 2:
|
785
788
|
key, value = opt.split("=")
|
786
|
-
|
789
|
+
if key in ("low_pass", "high_pass"):
|
790
|
+
value = float(value)
|
791
|
+
options[key] = value
|
792
|
+
else:
|
793
|
+
_logger.warning(
|
794
|
+
f"key {key} is not recognized by nabu. Ignore it."
|
795
|
+
)
|
787
796
|
else:
|
788
|
-
_logger.info(f"ignore option {opt}. Invalid
|
797
|
+
_logger.info(f"ignore option {opt}. Invalid syntax")
|
789
798
|
return options
|
790
|
-
|
791
|
-
@deprecated(replacement="get_nabu_cor_options_as_str", since_version="1.1")
|
792
|
-
def get_nabu_cor_options(self) -> str:
|
793
|
-
return self.get_nabu_cor_options_as_str()
|
794
|
-
|
795
|
-
def get_nabu_cor_options_as_str(self) -> str:
|
796
|
-
"""return cor option for nabu"""
|
797
|
-
|
798
|
-
def cast_key_value(key, value):
|
799
|
-
if key in ("side",):
|
800
|
-
return f"{key}='{value}'"
|
801
|
-
else:
|
802
|
-
return f"{key}={value}"
|
803
|
-
|
804
|
-
return " ; ".join(
|
805
|
-
[
|
806
|
-
cast_key_value(key, value)
|
807
|
-
for key, value in self.get_nabu_cor_options_as_dict().items()
|
808
|
-
]
|
809
|
-
)
|
@@ -42,6 +42,10 @@ from tomwer.core.process.reconstruction.nabu.utils import (
|
|
42
42
|
slice_index_to_int,
|
43
43
|
get_nabu_multicor_file_prefix,
|
44
44
|
)
|
45
|
+
from tomwer.core.process.reconstruction.utils.cor import (
|
46
|
+
relative_pos_to_absolute,
|
47
|
+
absolute_pos_to_relative,
|
48
|
+
)
|
45
49
|
from tomwer.core.process.reconstruction.nabu.target import Target
|
46
50
|
from tomwer.core.scan.scanbase import TomwerScanBase
|
47
51
|
from tomwer.core.utils.slurm import get_slurm_script_name, is_slurm_available
|
@@ -495,22 +499,6 @@ class _ReconstructorMultiCor(_NabuBaseReconstructor):
|
|
495
499
|
else:
|
496
500
|
raise ValueError(f"{self.target} is not recognized as a valid target")
|
497
501
|
|
498
|
-
@staticmethod
|
499
|
-
def convert_cor_from_rel_to_abs(scan, cor):
|
500
|
-
if scan.dim_1 is not None:
|
501
|
-
return cor + (scan.dim_1 - 1) / 2.0
|
502
|
-
else:
|
503
|
-
_logger.warning("enable to get image half width. Set it to 1024")
|
504
|
-
return cor + 1024
|
505
|
-
|
506
|
-
@staticmethod
|
507
|
-
def convert_cor_from_abs_to_rel(scan, cor):
|
508
|
-
if scan.dim_1 is not None:
|
509
|
-
return cor - (scan.dim_1 - 1) / 2.0
|
510
|
-
else:
|
511
|
-
_logger.warning("enable to get image half width. Set it to 1024")
|
512
|
-
return cor - 1024
|
513
|
-
|
514
502
|
def _run_nabu_multicor_locally(
|
515
503
|
self,
|
516
504
|
conf_file: str,
|
@@ -530,7 +518,10 @@ class _ReconstructorMultiCor(_NabuBaseReconstructor):
|
|
530
518
|
slice_index = slice_index_to_int(self.slice_index, scan=self.scan)
|
531
519
|
|
532
520
|
cor_in_nabu_ref = tuple(
|
533
|
-
[
|
521
|
+
[
|
522
|
+
relative_pos_to_absolute(relative_pos=cor, det_width=self.scan.dim_1)
|
523
|
+
for cor in self.cors
|
524
|
+
]
|
534
525
|
)
|
535
526
|
cor_in_nabu_ref = ",".join([str(cor) for cor in cor_in_nabu_ref])
|
536
527
|
command = " ".join(
|
@@ -567,12 +558,15 @@ class _ReconstructorMultiCor(_NabuBaseReconstructor):
|
|
567
558
|
scan=self.scan,
|
568
559
|
file_format=file_format,
|
569
560
|
cors=[
|
570
|
-
|
561
|
+
relative_pos_to_absolute(relative_pos=cor, det_width=self.scan.dim_1)
|
562
|
+
for cor in self.cors
|
571
563
|
],
|
572
564
|
)
|
573
565
|
# convert back from abs ref to rel ref
|
574
566
|
recons_vol_identifiers = {
|
575
|
-
|
567
|
+
absolute_pos_to_relative(
|
568
|
+
absolute_pos=cor, det_width=self.scan.dim_1
|
569
|
+
): identifiers
|
576
570
|
for cor, identifiers in recons_vol_identifiers.items()
|
577
571
|
}
|
578
572
|
return ResultsLocalRun(
|
@@ -622,7 +616,10 @@ class _ReconstructorMultiCor(_NabuBaseReconstructor):
|
|
622
616
|
|
623
617
|
slice_index = slice_index_to_int(self.slice_index, scan=self.scan)
|
624
618
|
cor_in_nabu_ref = tuple(
|
625
|
-
[
|
619
|
+
[
|
620
|
+
relative_pos_to_absolute(relative_pos=cor, det_width=self.scan.dim_1)
|
621
|
+
for cor in self.cors
|
622
|
+
]
|
626
623
|
)
|
627
624
|
cor_in_nabu_ref = ",".join([str(cor) for cor in cor_in_nabu_ref])
|
628
625
|
|
@@ -16,6 +16,7 @@ from tomwer.core.utils.scanutils import data_identifier_to_scan
|
|
16
16
|
from tomwer.core.utils.slurm import is_slurm_available
|
17
17
|
from tomwer.core.process.reconstruction.nabu.plane import NabuPlane
|
18
18
|
from tomwer.core.process.reconstruction.nabu.utils import slice_index_to_int
|
19
|
+
from tomwer.core.process.reconstruction.utils.cor import relative_pos_to_absolute
|
19
20
|
from tomwer.core.volume.volumefactory import VolumeFactory
|
20
21
|
from tomwer.core.process.reconstruction.output import (
|
21
22
|
PROCESS_FOLDER_RECONSTRUCTED_SLICES,
|
@@ -131,7 +132,10 @@ def run_slices_reconstruction(
|
|
131
132
|
if scan.axis_params is not None and scan.axis_params.relative_cor_value is not None:
|
132
133
|
if "reconstruction" in config:
|
133
134
|
# move the cor value to the nabu reference
|
134
|
-
cor_nabu_ref =
|
135
|
+
cor_nabu_ref = relative_pos_to_absolute(
|
136
|
+
relative_pos=scan.axis_params.relative_cor_value,
|
137
|
+
det_width=scan.dim_1,
|
138
|
+
)
|
135
139
|
config["reconstruction"]["rotation_axis_position"] = str(cor_nabu_ref)
|
136
140
|
_logger.info(f"set nabu reconstruction parameters to {scan}")
|
137
141
|
|
@@ -31,6 +31,7 @@ from tomwer.core.process.reconstruction.scores import (
|
|
31
31
|
get_disk_mask_radius,
|
32
32
|
)
|
33
33
|
from tomwer.core.process.reconstruction.scores.params import ScoreMethod
|
34
|
+
from tomwer.core.process.reconstruction.utils.cor import relative_pos_to_absolute
|
34
35
|
from tomwer.core.process.task import Task
|
35
36
|
from tomwer.core.scan.nxtomoscan import NXtomoScan
|
36
37
|
from tomwer.core.scan.scanbase import TomwerScanBase
|
@@ -53,7 +54,6 @@ from tomwer.core.process.reconstruction.nabu.utils import (
|
|
53
54
|
get_multi_cor_recons_volume_identifiers,
|
54
55
|
get_nabu_multicor_file_prefix,
|
55
56
|
)
|
56
|
-
from tomwer.core.process.reconstruction.nabu.nabuscores import _ReconstructorMultiCor
|
57
57
|
|
58
58
|
from .params import SAAxisParams
|
59
59
|
|
@@ -316,9 +316,9 @@ class SAAxisTask(
|
|
316
316
|
continue
|
317
317
|
|
318
318
|
for cor in cors:
|
319
|
-
cor_nabu_ref =
|
320
|
-
|
321
|
-
|
319
|
+
cor_nabu_ref = relative_pos_to_absolute(
|
320
|
+
relative_pos=scan.axis_params.relative_cor_value,
|
321
|
+
det_width=scan.dim_1,
|
322
322
|
)
|
323
323
|
volume_identifiers = get_multi_cor_recons_volume_identifiers(
|
324
324
|
scan=scan,
|
@@ -28,6 +28,7 @@ from tomwer.core.process.reconstruction.nabu.nabucommon import (
|
|
28
28
|
ResultsWithStd,
|
29
29
|
)
|
30
30
|
from tomwer.core.process.reconstruction.nabu.nabuslices import SingleSliceRunner
|
31
|
+
from tomwer.core.process.reconstruction.utils.cor import relative_pos_to_absolute
|
31
32
|
from tomwer.core.process.reconstruction.scores import (
|
32
33
|
ComputedScore,
|
33
34
|
ScoreMethod,
|
@@ -360,15 +361,14 @@ class SADeltaBetaTask(
|
|
360
361
|
|
361
362
|
def _config_preprocessing(self, scan, config, delta_beta_s, output_dir) -> dict:
|
362
363
|
config.get("phase", {}).pop("beam_shape", None)
|
363
|
-
# if scan contains some center of position copy it to nabu
|
364
364
|
if (
|
365
365
|
scan.axis_params is not None
|
366
366
|
and scan.axis_params.relative_cor_value is not None
|
367
367
|
):
|
368
368
|
if "reconstruction" in config:
|
369
|
-
|
370
|
-
|
371
|
-
scan.
|
369
|
+
cor_nabu_ref = relative_pos_to_absolute(
|
370
|
+
relative_pos=scan.axis_params.relative_cor_value,
|
371
|
+
det_width=scan.dim_1,
|
372
372
|
)
|
373
373
|
config["reconstruction"]["rotation_axis_position"] = str(cor_nabu_ref)
|
374
374
|
|
@@ -42,5 +42,5 @@ def test_read_x_rotation_axis_pixel_position(nxtomo_scan_360): # noqa F811
|
|
42
42
|
|
43
43
|
nxtomo_scan_360.clear_caches()
|
44
44
|
task.run()
|
45
|
-
assert nxtomo_scan_360.axis_params.absolute_cor_value == 22.
|
45
|
+
assert nxtomo_scan_360.axis_params.absolute_cor_value == 22.5
|
46
46
|
assert nxtomo_scan_360.axis_params.relative_cor_value == 12.5
|
@@ -10,11 +10,11 @@ def test_cor_conversion():
|
|
10
10
|
"""
|
11
11
|
test absolute_pos_to_relative and relative_pos_to_absolute functions
|
12
12
|
"""
|
13
|
-
assert relative_pos_to_absolute(relative_pos=0.0, det_width=100) ==
|
14
|
-
assert relative_pos_to_absolute(relative_pos=0.0, det_width=101) == 50.
|
13
|
+
assert relative_pos_to_absolute(relative_pos=0.0, det_width=100) == 50.0
|
14
|
+
assert relative_pos_to_absolute(relative_pos=0.0, det_width=101) == 50.5
|
15
15
|
|
16
|
-
assert absolute_pos_to_relative(absolute_pos=20, det_width=500) == -
|
17
|
-
assert absolute_pos_to_relative(absolute_pos=300, det_width=500) == 50.
|
16
|
+
assert absolute_pos_to_relative(absolute_pos=20, det_width=500) == -230.0
|
17
|
+
assert absolute_pos_to_relative(absolute_pos=300, det_width=500) == 50.0
|
18
18
|
|
19
19
|
for det_width in (10, 20, 30, 50):
|
20
20
|
for relative_cor_pos in (0, -2.3, -4.5):
|