tomwer 1.4.0__py3-none-any.whl → 1.4.0rc0__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 (53) hide show
  1. orangecontrib/tomwer/tutorials/test_cor.ows +3 -3
  2. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +14 -6
  3. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +2 -4
  4. tomwer/app/axis.py +3 -0
  5. tomwer/app/multipag.py +3 -11
  6. tomwer/core/process/reconstruction/axis/axis.py +736 -27
  7. tomwer/core/process/reconstruction/axis/mode.py +24 -86
  8. tomwer/core/process/reconstruction/axis/params.py +138 -127
  9. tomwer/core/process/reconstruction/nabu/nabuscores.py +22 -19
  10. tomwer/core/process/reconstruction/nabu/nabuslices.py +1 -5
  11. tomwer/core/process/reconstruction/saaxis/saaxis.py +4 -4
  12. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +4 -4
  13. tomwer/core/process/reconstruction/tests/test_axis.py +1 -1
  14. tomwer/core/process/reconstruction/tests/test_utils.py +4 -4
  15. tomwer/core/process/reconstruction/utils/cor.py +4 -8
  16. tomwer/core/process/tests/test_axis.py +231 -0
  17. tomwer/core/process/tests/test_nabu.py +3 -1
  18. tomwer/core/scan/nxtomoscan.py +0 -2
  19. tomwer/core/scan/scanbase.py +4 -4
  20. tomwer/core/scan/tests/test_process_registration.py +18 -0
  21. tomwer/gui/reconstruction/axis/AxisMainWindow.py +9 -20
  22. tomwer/gui/reconstruction/axis/AxisOptionsWidget.py +79 -239
  23. tomwer/gui/reconstruction/axis/AxisSettingsWidget.py +17 -38
  24. tomwer/gui/reconstruction/axis/AxisWidget.py +8 -16
  25. tomwer/gui/reconstruction/axis/CalculationWidget.py +200 -44
  26. tomwer/gui/reconstruction/axis/ControlWidget.py +2 -10
  27. tomwer/gui/reconstruction/axis/InputWidget.py +155 -11
  28. tomwer/gui/reconstruction/saaxis/corrangeselector.py +10 -19
  29. tomwer/gui/reconstruction/scores/scoreplot.py +2 -5
  30. tomwer/gui/reconstruction/tests/test_nabu.py +0 -8
  31. tomwer/gui/stitching/config/axisparams.py +0 -2
  32. tomwer/gui/stitching/z_stitching/fineestimation.py +1 -1
  33. tomwer/gui/tests/test_axis_gui.py +15 -31
  34. tomwer/synctools/stacks/reconstruction/axis.py +23 -5
  35. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +1 -1
  36. tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
  37. tomwer/synctools/stacks/reconstruction/normalization.py +1 -1
  38. tomwer/synctools/stacks/reconstruction/saaxis.py +1 -1
  39. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +1 -1
  40. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_axis.py +16 -0
  41. tomwer/tests/test_ewoks/test_single_node_execution.py +1 -1
  42. tomwer/tests/test_ewoks/test_workflows.py +1 -1
  43. tomwer/version.py +1 -1
  44. {tomwer-1.4.0.dist-info → tomwer-1.4.0rc0.dist-info}/METADATA +3 -3
  45. {tomwer-1.4.0.dist-info → tomwer-1.4.0rc0.dist-info}/RECORD +49 -52
  46. tomwer/core/process/reconstruction/axis/side.py +0 -8
  47. tomwer/gui/fonts.py +0 -5
  48. tomwer/gui/reconstruction/axis/EstimatedCORWidget.py +0 -394
  49. tomwer/gui/reconstruction/axis/EstimatedCorComboBox.py +0 -118
  50. {tomwer-1.4.0.dist-info → tomwer-1.4.0rc0.dist-info}/LICENSE +0 -0
  51. {tomwer-1.4.0.dist-info → tomwer-1.4.0rc0.dist-info}/WHEEL +0 -0
  52. {tomwer-1.4.0.dist-info → tomwer-1.4.0rc0.dist-info}/entry_points.txt +0 -0
  53. {tomwer-1.4.0.dist-info → tomwer-1.4.0rc0.dist-info}/top_level.txt +0 -0
@@ -5,11 +5,10 @@ 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
8
9
  from silx.utils.enum import Enum as _Enum
9
10
  from tomoscan.esrf.scan.utils import get_data
10
11
 
11
- from tomwer.core.process.reconstruction.utils.cor import relative_pos_to_absolute
12
- from tomwer.core.process.reconstruction.axis.side import Side
13
12
  from tomwer.core.scan.scanbase import TomwerScanBase
14
13
 
15
14
  from .anglemode import CorAngleMode
@@ -78,14 +77,13 @@ class AxisResource(object):
78
77
  """Paganin configuration for axis calculation. To simplify we have only one
79
78
  static configuration for now. Otherwise complicate stuff"""
80
79
 
81
- def __init__(self, url: None | DataUrl, angle: float | None = None):
80
+ def __init__(self, url):
82
81
  assert url is None or isinstance(url, DataUrl)
83
82
  assert url is None or url.is_valid()
84
83
  self.__url = url
85
84
  self.__raw_data = None
86
85
  self.__norme_data = None
87
86
  self.__norm_paganin = None
88
- self.__angle = angle
89
87
 
90
88
  def __str__(self):
91
89
  return f"{type(self)}, url: {self.__url.path() if self.__url else None}"
@@ -118,10 +116,6 @@ class AxisResource(object):
118
116
  def normalized_data(self, data):
119
117
  self.__norme_data = data
120
118
 
121
- @property
122
- def angle(self) -> float | None:
123
- return self.__angle
124
-
125
119
  def normalize_data(self, scan, log_):
126
120
  """Normalize data for axis calculation"""
127
121
  if not isinstance(scan, TomwerScanBase):
@@ -188,7 +182,7 @@ class AxisResource(object):
188
182
 
189
183
  class AxisRP:
190
184
  """
191
- Configuration class for a tomwer :class:`AxisTask`
185
+ Configuration class for a tomwer :class:`AxisProcess`
192
186
 
193
187
  note: every modification on the parameters will process a call fo changed
194
188
  except `axis_url_1` and `axis_url_2` which will produce a call to the
@@ -212,12 +206,11 @@ class AxisRP:
212
206
  "FINE_STEP_X",
213
207
  "SCALE_IMG2_TO_IMG1",
214
208
  "NEAR_POSITION",
215
- "MOTOR_OFFSET",
216
- "X_ROTATION_AXIS_PIXEL_POSITION",
217
209
  "SINOGRAM_SUBSAMPLING",
218
210
  "PADDING_MODE",
219
211
  "FLIP_LR",
220
212
  "COMPOSITE_OPTS",
213
+ "SIDE",
221
214
  "COR_OPTIONS",
222
215
  )
223
216
 
@@ -232,18 +225,17 @@ class AxisRP:
232
225
  None is not processing"""
233
226
  self.__angle_mode = CorAngleMode.use_0_180
234
227
  """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'"""
239
228
  self.__estimated_cor = 0.0
240
- """Estimated position of the center of rotation. Given by the user or computed from 'x_rotation_axis_pixel_position' and 'pixel_offset'"""
229
+ """Position to use for near calculation"""
241
230
  self.__axis_url_1 = AxisResource(url=None)
242
231
  """first data url to use for axis cor calculation"""
243
232
  self.__axis_url_2 = AxisResource(url=None)
244
233
  """second data url to use for axis cor calculation"""
245
234
  self.__calculation_input_type = AxisCalculationInput.transmission
246
235
  """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"""
247
239
  self.__sinogram_line = "middle"
248
240
  """Line of the radios to use for getting the sinogram"""
249
241
  self.__sinogram_subsampling = 10
@@ -252,14 +244,17 @@ class AxisRP:
252
244
  self.__look_at_stdmax = False
253
245
  """do the near search at X position which as the max Y column standard
254
246
  deviation"""
255
- self.__composite_window_size = 5
247
+ self.__near_wx = 5
256
248
  """do the near search in an X window of size +-near_wx"""
257
- self.__composite_fine_step = 0.1
249
+ self.__fine_stepx = 0.1
258
250
  """shift step x for fine shifting image"""
259
251
  self.__scale_img2_to_img1 = False
260
252
  """do image scaling"""
261
253
  self.__padding_mode = None
262
254
  self.__frame_width = None
255
+ self.__side = "right"
256
+ """side of the cor. Requested by nabu cor algorithms growing-window
257
+ and sliding-window"""
263
258
  self.__flip_lr = True
264
259
  self.__composite_options = {
265
260
  "theta": DEFAULT_CMP_THETA,
@@ -279,7 +274,37 @@ class AxisRP:
279
274
 
280
275
  @mode.setter
281
276
  def mode(self, mode):
282
- self.__mode = AxisMode.from_value(mode)
277
+ if isinstance(mode, str):
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
283
308
  self.changed()
284
309
 
285
310
  @property
@@ -321,7 +346,7 @@ class AxisRP:
321
346
  def set_relative_value(self, value):
322
347
  if not isinstance(value, (int, float, str, type(None))):
323
348
  raise TypeError(
324
- f"value is expected to be an instance of {int}, {float}, {str}, or {None}. {type(value)} provided"
349
+ f"value is expected to be an instance of {int} {float}, {str} or {None}. {type(value)} provided"
325
350
  )
326
351
  if value is None:
327
352
  changed = self.__relative_value is not None
@@ -336,46 +361,32 @@ class AxisRP:
336
361
  else:
337
362
  self.__relative_value = float(value)
338
363
  if self.frame_width is not None:
339
- self.__absolute_value = relative_pos_to_absolute(
340
- relative_pos=self.__relative_value, det_width=self.frame_width
364
+ self.__absolute_value = (
365
+ self.__relative_value + (self.frame_width - 1) / 2.0
341
366
  )
342
367
  self.changed()
343
368
 
344
369
  @property
345
- def estimated_cor(self) -> Side | float | None:
370
+ def estimated_cor(self):
346
371
  return self.__estimated_cor
347
372
 
348
373
  @estimated_cor.setter
349
- def estimated_cor(self, value: Side | float | None):
350
- try:
351
- value = Side.from_value(value)
352
- except ValueError:
353
- pass
374
+ def estimated_cor(self, value):
354
375
  if self.__estimated_cor != value:
355
376
  self.__estimated_cor = value
356
377
  self.changed()
357
378
 
358
379
  @property
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
380
+ def side(self):
381
+ return self.__side
382
+
383
+ @side.setter
384
+ def side(self, side):
385
+ if side not in ("all", "left", "right", "center", "near"):
386
+ raise ValueError(f"side '{side}' is not managed")
387
+ if self.__side != side:
388
+ self.__side = side
389
+ self.changed()
379
390
 
380
391
  @property
381
392
  def axis_url_1(self):
@@ -459,6 +470,16 @@ class AxisRP:
459
470
  self.__calculation_input_type = type_
460
471
  self.changed()
461
472
 
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
+
462
483
  @property
463
484
  def sinogram_line(self):
464
485
  return self.__sinogram_line
@@ -499,24 +520,23 @@ class AxisRP:
499
520
  self.__look_at_stdmax = stdmax
500
521
 
501
522
  @property
502
- def composite_window_size(self):
503
- return self.__composite_window_size
523
+ def near_wx(self):
524
+ return self.__near_wx
504
525
 
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
526
+ @near_wx.setter
527
+ def near_wx(self, width):
528
+ if self.__near_wx != width:
529
+ self.__near_wx = width
509
530
  self.changed()
510
531
 
511
532
  @property
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
533
+ def fine_step_x(self):
534
+ return self.__fine_stepx
535
+
536
+ @fine_step_x.setter
537
+ def fine_step_x(self, step_size):
538
+ if self.__fine_stepx != step_size:
539
+ self.__fine_stepx = step_size
520
540
  self.changed()
521
541
 
522
542
  @property
@@ -573,7 +593,7 @@ class AxisRP:
573
593
  "near_pos",
574
594
  "near_width",
575
595
  ):
576
- raise KeyError(f"{key} is not recognized")
596
+ raise KeyError(f"{key} is not recogized")
577
597
  self.__composite_options = opts
578
598
 
579
599
  @property
@@ -587,7 +607,7 @@ class AxisRP:
587
607
  self.changed()
588
608
 
589
609
  def changed(self):
590
- """callback to overwrite when the parameter value changed"""
610
+ """callback to overwrite when the paramter value changed"""
591
611
  pass
592
612
 
593
613
  def n_url(self):
@@ -620,27 +640,21 @@ class AxisRP:
620
640
  "POSITION_VALUE": self.relative_cor_value,
621
641
  "CALC_INPUT_TYPE": self.calculation_input_type.to_dict(),
622
642
  "ANGLE_MODE": self.angle_mode.value,
623
- "SINOGRAM_LINE": (
624
- self.sinogram_line if self.mode.requires_sinogram_index() else ""
625
- ),
643
+ "USE_SINOGRAM": self.use_sinogram,
644
+ "SINOGRAM_LINE": self.sinogram_line if self.use_sinogram else "",
626
645
  "SINOGRAM_SUBSAMPLING": self.sinogram_subsampling,
627
646
  "AXIS_URL_1": axis_urls_1,
628
647
  "AXIS_URL_2": axis_urls_2,
629
648
  "LOOK_AT_STDMAX": self.look_at_stdmax,
630
- "NEAR_WX": self.composite_window_size,
631
- "FINE_STEP_X": self.composite_fine_step,
649
+ "NEAR_WX": self.near_wx,
650
+ "FINE_STEP_X": self.fine_step_x,
632
651
  "SCALE_IMG2_TO_IMG1": self.scale_img2_to_img1,
633
- "NEAR_POSITION": (
634
- self.estimated_cor.value
635
- if isinstance(self.estimated_cor, Side)
636
- else self.estimated_cor
637
- ),
652
+ "NEAR_POSITION": self.estimated_cor,
638
653
  "PADDING_MODE": self.padding_mode,
639
654
  "FLIP_LR": self.flip_lr,
640
655
  "COMPOSITE_OPTS": self.composite_options,
656
+ "SIDE": self.side,
641
657
  "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,
644
658
  }
645
659
  return _dict
646
660
 
@@ -665,6 +679,8 @@ class AxisRP:
665
679
  self.calculation_input_type = AxisCalculationInput.from_value(
666
680
  _dict["CALC_INPUT_TYPE"]
667
681
  )
682
+ if "USE_SINOGRAM" in _dict:
683
+ self.use_sinogram = _dict["USE_SINOGRAM"]
668
684
  if "ANGLE_MODE" in _dict:
669
685
  self.angle_mode = CorAngleMode.from_value(_dict["ANGLE_MODE"])
670
686
  if "SINOGRAM_LINE" in _dict:
@@ -676,13 +692,13 @@ class AxisRP:
676
692
  if "LOOK_AT_STDMAX" in _dict:
677
693
  self.look_at_stdmax = _dict["LOOK_AT_STDMAX"]
678
694
  if "NEAR_WX" in _dict:
679
- self.composite_window_size = _dict["NEAR_WX"]
695
+ self.near_wx = _dict["NEAR_WX"]
680
696
  if "FINE_STEP_X" in _dict:
681
- self.composite_fine_step = _dict["FINE_STEP_X"]
697
+ self.fine_step_x = _dict["FINE_STEP_X"]
682
698
  if "SCALE_IMG2_TO_IMG1" in _dict:
683
699
  self.scale_img2_to_img1 = _dict["SCALE_IMG2_TO_IMG1"]
684
700
  if "NEAR_POSITION" in _dict:
685
- self.estimated_cor = _dict["NEAR_POSITION"]
701
+ self.estimated_cor = float(_dict["NEAR_POSITION"])
686
702
  if "SINOGRAM_SUBSAMPLING" in _dict:
687
703
  self.sinogram_subsampling = _dict["SINOGRAM_SUBSAMPLING"]
688
704
  if "PADDING_MODE" in _dict:
@@ -691,6 +707,8 @@ class AxisRP:
691
707
  self.flip_lr = bool(_dict["FLIP_LR"])
692
708
  if "COMPOSITE_OPTS" in _dict:
693
709
  self.composite_options = _dict["COMPOSITE_OPTS"]
710
+ if "SIDE" in _dict:
711
+ self.side = _dict["SIDE"]
694
712
  self.extra_cor_options = _dict.get("COR_OPTIONS", "")
695
713
 
696
714
  def copy(self, axis_params, copy_axis_url=True, copy_flip_lr=True):
@@ -699,17 +717,17 @@ class AxisRP:
699
717
  self.frame_width = axis_params.frame_width
700
718
  self.set_relative_value(axis_params.relative_cor_value)
701
719
  self.calculation_input_type = axis_params.calculation_input_type
720
+ self.use_sinogram = axis_params.use_sinogram
702
721
  self.angle_mode = axis_params.angle_mode
703
722
  self.sinogram_line = axis_params.sinogram_line
704
723
  self.sinogram_subsampling = axis_params.sinogram_subsampling
705
724
  self.look_at_stdmax = axis_params.look_at_stdmax
706
- self.composite_window_size = axis_params.composite_window_size
707
- self.composite_fine_step = axis_params.composite_fine_step
725
+ self.near_wx = axis_params.near_wx
726
+ self.fine_step_x = axis_params.fine_step_x
708
727
  self.scale_img2_to_img1 = axis_params.scale_img2_to_img1
709
728
  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
712
729
  self.padding_mode = axis_params.padding_mode
730
+ self.side = axis_params.side
713
731
  self.composite_options = axis_params.composite_options
714
732
  self.extra_cor_options = axis_params.extra_cor_options
715
733
  if copy_axis_url:
@@ -736,63 +754,56 @@ class AxisRP:
736
754
  AxisMode.sliding_window_radios,
737
755
  AxisMode.sliding_window_sinogram,
738
756
  ):
739
- extra_info = f"side: {self.estimated_cor}"
757
+ extra_info = f"side: {self.side}, use sinogram: {self.use_sinogram}"
740
758
  results = ", ".join((results, extra_info))
741
759
  return results
742
760
 
743
761
  def get_nabu_cor_options_as_dict(self) -> str:
744
762
  options = {}
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
- )
763
+ if self.mode is AxisMode.near:
764
+ self.side = "near"
765
+ request_side = len(AXIS_MODE_METADATAS[self.mode].valid_sides) > 0
766
+ if request_side:
767
+
768
+ if self.side == "near":
769
+ options["side"] = self.composite_options.get(
770
+ "near_pos", self.estimated_cor
771
+ )
772
+ else:
773
+ options["side"] = self.side
770
774
 
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
775
+ if self.side == "near":
776
+ near_width = self.composite_options.get("near_width", 20.0)
777
+ options["near_width"] = near_width
780
778
 
781
779
  # append "extra_cor_options" to already handled cor options
782
- # expected values: low_pass, high_pass
783
780
  extra_cor_options = self.extra_cor_options.replace(" ", "")
784
781
 
785
782
  if extra_cor_options != "":
786
783
  for opt in self.extra_cor_options.replace(" ", "").split(";"):
787
784
  if len(opt.split("=")) == 2:
788
785
  key, value = opt.split("=")
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
- )
786
+ options[key] = value
796
787
  else:
797
- _logger.info(f"ignore option {opt}. Invalid syntax")
788
+ _logger.info(f"ignore option {opt}. Invalid synthax")
798
789
  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,10 +42,6 @@ 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
- )
49
45
  from tomwer.core.process.reconstruction.nabu.target import Target
50
46
  from tomwer.core.scan.scanbase import TomwerScanBase
51
47
  from tomwer.core.utils.slurm import get_slurm_script_name, is_slurm_available
@@ -287,7 +283,7 @@ class _Reconstructor(_NabuBaseReconstructor):
287
283
  )
288
284
 
289
285
  results = {}
290
- if self.advancement is not None:
286
+ if self.advancement:
291
287
  self.advancement.total = len(self.nabu_configs)
292
288
  for var_value, config in self.nabu_configs.items():
293
289
  if self._cancelled:
@@ -312,7 +308,7 @@ class _Reconstructor(_NabuBaseReconstructor):
312
308
  process_name=self.process_name,
313
309
  )
314
310
  # specific treatment for cor: rename output files
315
- if self.advancement is not None:
311
+ if self.advancement:
316
312
  self.advancement.update()
317
313
  return results
318
314
 
@@ -499,6 +495,22 @@ class _ReconstructorMultiCor(_NabuBaseReconstructor):
499
495
  else:
500
496
  raise ValueError(f"{self.target} is not recognized as a valid target")
501
497
 
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
+
502
514
  def _run_nabu_multicor_locally(
503
515
  self,
504
516
  conf_file: str,
@@ -518,10 +530,7 @@ class _ReconstructorMultiCor(_NabuBaseReconstructor):
518
530
  slice_index = slice_index_to_int(self.slice_index, scan=self.scan)
519
531
 
520
532
  cor_in_nabu_ref = tuple(
521
- [
522
- relative_pos_to_absolute(relative_pos=cor, det_width=self.scan.dim_1)
523
- for cor in self.cors
524
- ]
533
+ [self.convert_cor_from_rel_to_abs(self.scan, cor) for cor in self.cors]
525
534
  )
526
535
  cor_in_nabu_ref = ",".join([str(cor) for cor in cor_in_nabu_ref])
527
536
  command = " ".join(
@@ -558,15 +567,12 @@ class _ReconstructorMultiCor(_NabuBaseReconstructor):
558
567
  scan=self.scan,
559
568
  file_format=file_format,
560
569
  cors=[
561
- relative_pos_to_absolute(relative_pos=cor, det_width=self.scan.dim_1)
562
- for cor in self.cors
570
+ self.convert_cor_from_rel_to_abs(self.scan, cor) for cor in self.cors
563
571
  ],
564
572
  )
565
573
  # convert back from abs ref to rel ref
566
574
  recons_vol_identifiers = {
567
- absolute_pos_to_relative(
568
- absolute_pos=cor, det_width=self.scan.dim_1
569
- ): identifiers
575
+ self.convert_cor_from_abs_to_rel(self.scan, cor): identifiers
570
576
  for cor, identifiers in recons_vol_identifiers.items()
571
577
  }
572
578
  return ResultsLocalRun(
@@ -616,10 +622,7 @@ class _ReconstructorMultiCor(_NabuBaseReconstructor):
616
622
 
617
623
  slice_index = slice_index_to_int(self.slice_index, scan=self.scan)
618
624
  cor_in_nabu_ref = tuple(
619
- [
620
- relative_pos_to_absolute(relative_pos=cor, det_width=self.scan.dim_1)
621
- for cor in self.cors
622
- ]
625
+ [self.convert_cor_from_rel_to_abs(self.scan, cor) for cor in self.cors]
623
626
  )
624
627
  cor_in_nabu_ref = ",".join([str(cor) for cor in cor_in_nabu_ref])
625
628
 
@@ -16,7 +16,6 @@ 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
20
19
  from tomwer.core.volume.volumefactory import VolumeFactory
21
20
  from tomwer.core.process.reconstruction.output import (
22
21
  PROCESS_FOLDER_RECONSTRUCTED_SLICES,
@@ -132,10 +131,7 @@ def run_slices_reconstruction(
132
131
  if scan.axis_params is not None and scan.axis_params.relative_cor_value is not None:
133
132
  if "reconstruction" in config:
134
133
  # move the cor value to the nabu reference
135
- cor_nabu_ref = relative_pos_to_absolute(
136
- relative_pos=scan.axis_params.relative_cor_value,
137
- det_width=scan.dim_1,
138
- )
134
+ cor_nabu_ref = scan.axis_params.relative_cor_value + (scan.dim_1 - 1) / 2.0
139
135
  config["reconstruction"]["rotation_axis_position"] = str(cor_nabu_ref)
140
136
  _logger.info(f"set nabu reconstruction parameters to {scan}")
141
137
 
@@ -31,7 +31,6 @@ 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
35
34
  from tomwer.core.process.task import Task
36
35
  from tomwer.core.scan.nxtomoscan import NXtomoScan
37
36
  from tomwer.core.scan.scanbase import TomwerScanBase
@@ -54,6 +53,7 @@ from tomwer.core.process.reconstruction.nabu.utils import (
54
53
  get_multi_cor_recons_volume_identifiers,
55
54
  get_nabu_multicor_file_prefix,
56
55
  )
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 = relative_pos_to_absolute(
320
- relative_pos=cor,
321
- det_width=scan.dim_1,
319
+ cor_nabu_ref = _ReconstructorMultiCor.convert_cor_from_rel_to_abs(
320
+ scan=scan,
321
+ cor=cor,
322
322
  )
323
323
  volume_identifiers = get_multi_cor_recons_volume_identifiers(
324
324
  scan=scan,
@@ -28,7 +28,6 @@ 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
32
31
  from tomwer.core.process.reconstruction.scores import (
33
32
  ComputedScore,
34
33
  ScoreMethod,
@@ -361,14 +360,15 @@ class SADeltaBetaTask(
361
360
 
362
361
  def _config_preprocessing(self, scan, config, delta_beta_s, output_dir) -> dict:
363
362
  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
- cor_nabu_ref = relative_pos_to_absolute(
370
- relative_pos=scan.axis_params.relative_cor_value,
371
- det_width=scan.dim_1,
369
+ # move the cor value to the nabu reference
370
+ cor_nabu_ref = (
371
+ scan.axis_params.relative_cor_value + (scan.dim_1 - 1) / 2.0
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.5
45
+ assert nxtomo_scan_360.axis_params.absolute_cor_value == 22.0
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) == 50.0
14
- assert relative_pos_to_absolute(relative_pos=0.0, det_width=101) == 50.5
13
+ assert relative_pos_to_absolute(relative_pos=0.0, det_width=100) == 49.5
14
+ assert relative_pos_to_absolute(relative_pos=0.0, det_width=101) == 50.0
15
15
 
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
16
+ assert absolute_pos_to_relative(absolute_pos=20, det_width=500) == -229.5
17
+ assert absolute_pos_to_relative(absolute_pos=300, det_width=500) == 50.5
18
18
 
19
19
  for det_width in (10, 20, 30, 50):
20
20
  for relative_cor_pos in (0, -2.3, -4.5):