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.
Files changed (51) hide show
  1. orangecontrib/tomwer/tutorials/test_cor.ows +3 -3
  2. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +6 -14
  3. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +4 -2
  4. tomwer/app/axis.py +0 -3
  5. tomwer/app/multipag.py +11 -3
  6. tomwer/core/process/reconstruction/axis/axis.py +27 -736
  7. tomwer/core/process/reconstruction/axis/mode.py +86 -24
  8. tomwer/core/process/reconstruction/axis/params.py +127 -138
  9. tomwer/core/process/reconstruction/axis/side.py +8 -0
  10. tomwer/core/process/reconstruction/nabu/nabuscores.py +17 -20
  11. tomwer/core/process/reconstruction/nabu/nabuslices.py +5 -1
  12. tomwer/core/process/reconstruction/saaxis/saaxis.py +4 -4
  13. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +4 -4
  14. tomwer/core/process/reconstruction/tests/test_axis.py +1 -1
  15. tomwer/core/process/reconstruction/tests/test_utils.py +4 -4
  16. tomwer/core/process/reconstruction/utils/cor.py +8 -4
  17. tomwer/core/process/tests/test_nabu.py +1 -3
  18. tomwer/core/scan/scanbase.py +4 -4
  19. tomwer/core/scan/tests/test_process_registration.py +0 -18
  20. tomwer/gui/fonts.py +5 -0
  21. tomwer/gui/reconstruction/axis/AxisMainWindow.py +20 -9
  22. tomwer/gui/reconstruction/axis/AxisOptionsWidget.py +239 -79
  23. tomwer/gui/reconstruction/axis/AxisSettingsWidget.py +38 -17
  24. tomwer/gui/reconstruction/axis/AxisWidget.py +16 -8
  25. tomwer/gui/reconstruction/axis/CalculationWidget.py +40 -200
  26. tomwer/gui/reconstruction/axis/ControlWidget.py +10 -2
  27. tomwer/gui/reconstruction/axis/EstimatedCORWidget.py +383 -0
  28. tomwer/gui/reconstruction/axis/EstimatedCorComboBox.py +118 -0
  29. tomwer/gui/reconstruction/axis/InputWidget.py +11 -155
  30. tomwer/gui/reconstruction/saaxis/corrangeselector.py +19 -10
  31. tomwer/gui/reconstruction/scores/scoreplot.py +5 -2
  32. tomwer/gui/reconstruction/tests/test_nabu.py +8 -0
  33. tomwer/gui/stitching/z_stitching/fineestimation.py +1 -1
  34. tomwer/gui/tests/test_axis_gui.py +31 -15
  35. tomwer/synctools/stacks/reconstruction/axis.py +5 -23
  36. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +1 -1
  37. tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
  38. tomwer/synctools/stacks/reconstruction/normalization.py +1 -1
  39. tomwer/synctools/stacks/reconstruction/saaxis.py +1 -1
  40. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +1 -1
  41. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_axis.py +0 -16
  42. tomwer/tests/test_ewoks/test_single_node_execution.py +1 -1
  43. tomwer/tests/test_ewoks/test_workflows.py +1 -1
  44. tomwer/version.py +1 -1
  45. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/METADATA +2 -2
  46. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/RECORD +50 -47
  47. tomwer/core/process/tests/test_axis.py +0 -231
  48. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/LICENSE +0 -0
  49. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/WHEEL +0 -0
  50. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/entry_points.txt +0 -0
  51. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc1.dist-info}/top_level.txt +0 -0
@@ -131,18 +131,15 @@ class AxisWidget(qt.QMainWindow):
131
131
  if mode is axis_mode.AxisMode.manual:
132
132
  self._setModeLockFrmSettings(False)
133
133
 
134
- def updateAutomaticallyEstimatedCor(self):
135
- return self._settingsWidget.updateAutomaticallyEstimatedCor()
136
-
137
- def setUpdateAutomaticallyEstimatedCor(self, value):
138
- self._settingsWidget.setUpdateAutomaticallyEstimatedCor(value)
139
-
140
134
  def setEstimatedCor(self, value):
141
135
  self._settingsWidget.setEstimatedCor(value=value)
142
136
 
143
137
  def getEstimatedCor(self):
144
138
  return self._settingsWidget.getEstimatedCor()
145
139
 
140
+ def updateXRotationAxisPixelPositionOnNewScan(self) -> bool:
141
+ return self._settingsWidget.updateXRotationAxisPixelPositionOnNewScan()
142
+
146
143
  def _setModeLockFrmSettings(self, lock: bool):
147
144
  # only lock the push button
148
145
  with block_signals(self):
@@ -233,9 +230,12 @@ class AxisWidget(qt.QMainWindow):
233
230
 
234
231
  if self._scan is not None:
235
232
  self._scan.axis_params.sigAxisUrlChanged.disconnect(self._updatePlot)
233
+ update_x_rotation_axis_pixel_position = (
234
+ self._settingsWidget._mainWidget.updateXRotationAxisPixelPositionOnNewScan()
235
+ )
236
236
  if (
237
- scan.x_rotation_axis_pixel_position is not None
238
- and self.updateAutomaticallyEstimatedCor()
237
+ update_x_rotation_axis_pixel_position
238
+ and scan.x_rotation_axis_pixel_position is not None
239
239
  ):
240
240
  self.setEstimatedCor(scan.x_rotation_axis_pixel_position)
241
241
 
@@ -495,6 +495,8 @@ class AxisWidget(qt.QMainWindow):
495
495
  if self._imgA is not None and self._imgB is not None:
496
496
  self.setImages(imgA=self._imgA, imgB=self._imgB, flipB=self._flipB)
497
497
 
498
+ # expose API
499
+
498
500
  def getXShift(self):
499
501
  return self._settingsWidget.getXShift()
500
502
 
@@ -524,3 +526,9 @@ class AxisWidget(qt.QMainWindow):
524
526
 
525
527
  def setModeLock(self, mode):
526
528
  return self._settingsWidget.setModeLock(mode=mode)
529
+
530
+ def isYAxisInverted(self) -> bool:
531
+ return self._settingsWidget.isYAxisInverted()
532
+
533
+ def setYAxisInverted(self, checked: bool):
534
+ return self._settingsWidget.setYAxisInverted(checked=checked)
@@ -4,11 +4,13 @@ import logging
4
4
 
5
5
  from silx.gui import qt
6
6
 
7
+ from tomwer.core.scan.scanbase import TomwerScanBase
7
8
  from tomwer.core.process.reconstruction.axis import mode as axis_mode
8
9
  from tomwer.gui.utils.buttons import PadlockButton
9
10
  from tomwer.gui.utils.qt_utils import block_signals
10
11
  from tomwer.synctools.axis import QAxisRP
11
12
  from tomwer.gui.utils.scrollarea import QComboBoxIgnoreWheel
13
+ from tomwer.gui.reconstruction.axis.EstimatedCORWidget import EstimatedCORWidget
12
14
 
13
15
  _logger = logging.getLogger(__name__)
14
16
 
@@ -24,6 +26,8 @@ class CalculationWidget(qt.QWidget):
24
26
 
25
27
  sigLockModeChanged = qt.Signal(bool)
26
28
  """signal emitted when the mode has been lock or unlock"""
29
+ sigUpdateXRotAxisPixelPosOnNewScan = qt.Signal()
30
+ sigYAxisInvertedChanged = qt.Signal(bool)
27
31
 
28
32
  def __init__(self, parent, axis_params):
29
33
  assert isinstance(axis_params, QAxisRP)
@@ -31,6 +35,7 @@ class CalculationWidget(qt.QWidget):
31
35
  self._axis_params = None
32
36
  self.setLayout(qt.QVBoxLayout())
33
37
 
38
+ # algorithm
34
39
  self._modeWidget = qt.QWidget(parent=self)
35
40
  self._modeWidget.setLayout(qt.QHBoxLayout())
36
41
  self.layout().addWidget(self._modeWidget)
@@ -53,9 +58,9 @@ class CalculationWidget(qt.QWidget):
53
58
  axis_mode.AxisMode.growing_window_sinogram,
54
59
  axis_mode.AxisMode.sino_coarse_to_fine,
55
60
  axis_mode.AxisMode.sliding_window_sinogram,
56
- axis_mode.AxisMode.sino_fourier_angles,
61
+ axis_mode.AxisMode.fourier_angles,
57
62
  ),
58
- # composite corase to fine
63
+ # composite coarse to fine
59
64
  (
60
65
  axis_mode.AxisMode.composite_coarse_to_fine,
61
66
  axis_mode.AxisMode.near,
@@ -91,181 +96,52 @@ class CalculationWidget(qt.QWidget):
91
96
  )
92
97
  self._modeWidget.layout().addWidget(self._lockMethodPB)
93
98
 
94
- self._optsWidget = qt.QWidget(self)
95
- self._optsWidget.setLayout(qt.QVBoxLayout())
96
- self.layout().addWidget(self._optsWidget)
97
-
98
- # padding option
99
- self._padding_widget = qt.QGroupBox("padding mode")
100
- self._padding_widget.setCheckable(True)
101
- self._optsWidget.layout().addWidget(self._padding_widget)
102
- self._padding_widget.setLayout(qt.QHBoxLayout())
103
-
104
- self._qbPaddingMode = QComboBoxIgnoreWheel(self._padding_widget)
105
- for _mode in (
106
- "constant",
107
- "edge",
108
- "linear_ramp",
109
- "maximum",
110
- "mean",
111
- "median",
112
- "minimum",
113
- "reflect",
114
- "symmetric",
115
- "wrap",
116
- ):
117
- self._qbPaddingMode.addItem(_mode)
118
- def_index = self._qbPaddingMode.findText("edge")
119
- self._qbPaddingMode.setCurrentIndex(def_index)
120
- self._padding_widget.layout().addWidget(self._qbPaddingMode)
121
-
122
- # side option
123
- self._sideWidget = qt.QWidget(self)
124
- self._sideWidget.setLayout(qt.QHBoxLayout())
125
- self._optsWidget.layout().addWidget(self._sideWidget)
126
- self._sideWidget.layout().addWidget(qt.QLabel("side:", self))
127
- self._sideCB = QComboBoxIgnoreWheel(self._optsWidget)
128
- self._sideWidget.layout().addWidget(self._sideCB)
129
- self._sideCB.setToolTip(
130
- "Define a side for the sliding and growing" "window algorithms"
131
- )
132
-
133
- # near mode options
134
- self._nearOptsWidget = qt.QWidget(parent=self)
135
- self._nearOptsWidget.setLayout(qt.QVBoxLayout())
136
- self._optsWidget.layout().addWidget(self._nearOptsWidget)
137
-
138
- # near value lock button
139
- self._nearValueCB = qt.QCheckBox(
140
- "Update automatically if `x_rotation_axis_pixel_position` found"
141
- )
142
- self._nearValueCB.setToolTip(
143
- "If the acquisition contains an "
144
- "estimation of the COR value then "
145
- "will set it automatically as estimated "
146
- "value."
147
- )
148
- self._nearOptsWidget.layout().addWidget(self._nearValueCB)
149
-
150
- # LineEdit position value
151
- self._qleValueW = qt.QWidget(self._nearOptsWidget)
152
- self._qleValueW.setLayout(qt.QFormLayout())
153
- self._nearOptsWidget.layout().addWidget(self._qleValueW)
154
-
155
- self._qleNearPosQLE = qt.QLineEdit("0", self._nearOptsWidget)
156
- validator = qt.QDoubleValidator(self._qleNearPosQLE)
157
- self._qleNearPosQLE.setValidator(validator)
158
- self._qleValueW.layout().addRow(
159
- "estimated value (in relative):", self._qleNearPosQLE
160
- )
161
-
162
- # cor_options
163
- self._corOptsWidget = qt.QWidget(self)
164
- self._corOptsWidget.setLayout(qt.QHBoxLayout())
165
- self._corOpts = qt.QLineEdit(self)
166
- self._corOpts.setToolTip(
167
- "Options for methods finding automatically the rotation axis position. 'side', 'near_pos' and 'near_width' are already provided by dedicated interface. The parameters are separated by commas and passed as 'name=value'. Mind the semicolon separator (;)."
168
- )
169
- self._corOpts.setPlaceholderText("low_pass=1; high_pass=20")
170
- self._corOptsWidget.layout().addWidget(qt.QLabel("cor advanced options"))
171
- self._corOptsWidget.layout().addWidget(self._corOpts)
172
- self._optsWidget.layout().addWidget(self._corOptsWidget)
99
+ # estimated cor
100
+ self._estimatedCorWidget = EstimatedCORWidget(self, axis_params=axis_params)
101
+ self.layout().addWidget(self._estimatedCorWidget)
173
102
 
174
103
  # connect signal / slot
175
104
  self._qcbPosition.currentIndexChanged.connect(self._modeChanged)
176
- self._qleNearPosQLE.editingFinished.connect(self._nearValueChanged)
177
- self._sideCB.currentTextChanged.connect(self._sideChanged)
178
105
  self._lockMethodPB.sigLockChanged.connect(self.lockMode)
179
- self._qbPaddingMode.currentIndexChanged.connect(self._paddingModeChanged)
180
- self._padding_widget.toggled.connect(self._paddingModeChanged)
181
- self._corOpts.editingFinished.connect(self._corOptsChanged)
106
+ self._estimatedCorWidget.sigUpdateXRotAxisPixelPosOnNewScan.connect(
107
+ self.sigUpdateXRotAxisPixelPosOnNewScan
108
+ )
109
+ self._estimatedCorWidget.sigYAxisInvertedChanged.connect(
110
+ self.sigYAxisInvertedChanged
111
+ )
182
112
 
183
113
  # set up interface
184
- self._sideWidget.setVisible(False)
114
+ self._estimatedCorWidget._updateVisibleSides(mode=self.getMode())
185
115
  self.setAxisParams(axis_params)
186
- self._nearValueCB.setChecked(True)
187
- self._nearOptsWidget.setHidden(True)
188
116
 
189
117
  def getMethodLockPB(self) -> qt.QPushButton:
190
118
  return self._lockMethodPB
191
119
 
192
120
  def setEstimatedCorValue(self, value):
193
- if value is not None:
194
- self._qleNearPosQLE.setText(str(value))
195
- # note: keep self._axis_params up to date.
196
- if self._axis_params:
197
- self._axis_params.estimated_cor = value
121
+ self._estimatedCorWidget.setEstimatedCor(value=value)
122
+ # note: force to update the side values.
123
+ self._estimatedCorWidget._updateVisibleSides(mode=self.getMode())
198
124
 
199
125
  def getEstimatedCor(self):
200
- try:
201
- return float(self._qleNearPosQLE.text())
202
- except ValueError:
203
- return 0
204
-
205
- def updateAutomaticallyEstimatedCor(self):
206
- return self._nearValueCB.isChecked()
126
+ return self._estimatedCorWidget.getEstimatedCor()
207
127
 
208
- def setUpdateAutomaticallyEstimatedCor(self, checked):
209
- self._nearValueCB.setChecked(checked)
128
+ def updateXRotationAxisPixelPositionOnNewScan(self) -> bool:
129
+ return self._estimatedCorWidget.updateXRotationAxisPixelPositionOnNewScan()
210
130
 
211
- def setSide(self, side):
212
- if side is not None:
213
- idx = self._sideCB.findText(side)
214
- if idx >= 0:
215
- self._sideCB.setCurrentIndex(idx)
216
-
217
- def getSide(self):
218
- return self._sideCB.currentText()
131
+ def setUpdateXRotationAxisPixelPositionOnNewScan(self, update: bool):
132
+ self._estimatedCorWidget.setUpdateXRotationAxisPixelPositionOnNewScan(
133
+ update=update
134
+ )
219
135
 
220
136
  def _modeChanged(self, *args, **kwargs):
221
137
  mode = self.getMode()
222
138
  with block_signals(self._qcbPosition):
223
139
  with block_signals(self._axis_params):
224
- self._corOptsWidget.setVisible(
225
- mode
226
- not in (
227
- axis_mode.AxisMode.manual,
228
- axis_mode.AxisMode.read,
229
- )
230
- )
231
-
232
- self._padding_widget.setVisible(
233
- axis_mode.AXIS_MODE_METADATAS[mode].allows_padding
234
- )
235
- if axis_mode.AXIS_MODE_METADATAS[mode].is_lockable:
236
- self._lockMethodPB.setVisible(True)
237
- else:
238
- self._lockMethodPB.setVisible(False)
239
- self.lockMode(False)
240
-
241
- sides_visible = len(axis_mode.AXIS_MODE_METADATAS[mode].valid_sides) > 0
242
- self._sideWidget.setVisible(sides_visible)
243
- if sides_visible is True:
244
- self._updateSideVisible(mode)
245
- self._nearOptsWidget.setVisible(self.getSide() == "near")
140
+ self._estimatedCorWidget._updateVisibleSides(mode)
246
141
  self._axis_params.mode = mode.value
247
142
  self._axis_params.changed()
248
143
  self.sigModeChanged.emit(mode.value)
249
144
 
250
- def _updateSideVisible(self, mode: axis_mode.AxisMode):
251
- mode = axis_mode.AxisMode.from_value(mode)
252
- if len(axis_mode.AXIS_MODE_METADATAS[mode].valid_sides) == 0:
253
- return
254
- else:
255
- current_value = self._axis_params.side
256
- with block_signals(self._sideCB):
257
- self._sideCB.clear()
258
- values = axis_mode.AXIS_MODE_METADATAS[mode].valid_sides
259
- for value in values:
260
- self._sideCB.addItem(value)
261
- idx = self._sideCB.findText(current_value)
262
- if idx == -1:
263
- # if side doesn't exists, propose right as default when possible
264
- idx = self._sideCB.findText("right")
265
- if idx >= 0:
266
- self._sideCB.setCurrentIndex(idx)
267
- self._axis_params.side = self.getSide()
268
-
269
145
  def isModeLock(self):
270
146
  return self._lockMethodPB.isLocked()
271
147
 
@@ -303,7 +179,7 @@ class CalculationWidget(qt.QWidget):
303
179
  """Return algorithm to use for axis calculation"""
304
180
  return axis_mode.AxisMode.from_value(self._qcbPosition.currentText())
305
181
 
306
- def setMode(self, mode):
182
+ def setMode(self, mode: axis_mode.AxisMode):
307
183
  with block_signals(self._qcbPosition):
308
184
  index = self._qcbPosition.findText(mode.value)
309
185
  if index >= 0:
@@ -311,43 +187,13 @@ class CalculationWidget(qt.QWidget):
311
187
  else:
312
188
  raise ValueError("Unable to find mode", mode)
313
189
  self._lockMethodPB.setVisible(mode not in (axis_mode.AxisMode.manual,))
314
-
315
- def _nearValueChanged(self, *args, **kwargs):
316
- self._axis_params.estimated_cor = self.getEstimatedCor()
317
-
318
- def _paddingModeChanged(self, *args, **kwargs):
319
- self._axis_params.padding_mode = self.getPaddingMode()
320
-
321
- def _corOptsChanged(self, *args, **kwargs):
322
- self._axis_params.extra_cor_options = self.getCorOptions()
323
-
324
- def _sideChanged(self, *args, **kwargs):
325
- side = self.getSide()
326
- if side not in ("", None):
327
- self._axis_params.side = side
328
- self._nearOptsWidget.setVisible(side == "near")
329
-
330
- def getCorOptions(self):
331
- return self._corOpts.text()
332
-
333
- def setCorOptions(self, opts: str | None):
334
- with block_signals(self._axis_params):
335
- self._corOpts.clear()
336
- if opts:
337
- self._corOpts.setText(opts)
338
- self._corOptsChanged()
339
-
340
- def getPaddingMode(self):
341
- if self._padding_widget.isChecked():
342
- return self._qbPaddingMode.currentText()
343
- else:
344
- return None
345
-
346
- def setPaddingMode(self, mode):
347
- index = self._qbPaddingMode.findText(mode)
348
- if index >= 0:
349
- self._qbPaddingMode.setCurrentIndex(index)
350
- self._paddingModeChanged()
190
+ mode_metadata = axis_mode.AXIS_MODE_METADATAS[mode]
191
+ estimated_cor_widget_visible = (
192
+ mode_metadata.allows_estimated_cor_as_numerical_value
193
+ or len(mode_metadata.valid_sides) > 0
194
+ )
195
+ self._estimatedCorWidget.setVisible(estimated_cor_widget_visible)
196
+ self._estimatedCorWidget._updateVisibleSides(mode=mode)
351
197
 
352
198
  def setAxisParams(self, axis):
353
199
  with block_signals(self):
@@ -359,16 +205,10 @@ class CalculationWidget(qt.QWidget):
359
205
  self._axis_params.mode = axis_mode.AxisMode.growing_window_radios
360
206
  self._axis_params.sigChanged.connect(self._axis_params_changed)
361
207
  self._axis_params_changed()
362
- self._sideChanged()
363
208
 
364
209
  def _axis_params_changed(self, *args, **kwargs):
365
210
  self.setMode(self._axis_params.mode)
366
- self.setEstimatedCorValue(self._axis_params.estimated_cor)
367
- self.setSide(self._axis_params.side)
368
- sides_visible = (
369
- len(axis_mode.AXIS_MODE_METADATAS[self._axis_params.mode].valid_sides) > 0
370
- )
371
- self._sideWidget.setVisible(sides_visible)
372
- self._updateSideVisible(mode=self._axis_params.mode)
373
- self.setPaddingMode(self._axis_params.padding_mode)
374
- self.setCorOptions(self._axis_params.extra_cor_options)
211
+
212
+ def setScan(self, scan: TomwerScanBase | None):
213
+ self._estimatedCorWidget.setPixelSize(pixel_size_m=scan.x_pixel_size)
214
+ self._estimatedCorWidget.setImageWidth(image_width=scan.dim_1)
@@ -5,6 +5,10 @@ import numpy
5
5
 
6
6
  from silx.gui import qt
7
7
 
8
+ from tomwer.core.process.reconstruction.utils.cor import (
9
+ absolute_pos_to_relative,
10
+ relative_pos_to_absolute,
11
+ )
8
12
  from tomwer.gui.utils.buttons import PadlockButton
9
13
  from tomwer.gui.settings import EDITING_BACKGROUND_COLOR
10
14
  from tomwer.synctools.axis import QAxisRP
@@ -263,7 +267,9 @@ class _PositionInfoWidget(qt.QWidget):
263
267
  except Exception:
264
268
  return
265
269
  else:
266
- abs_value = float(rel_value) + (width - 1) / 2.0
270
+ abs_value = relative_pos_to_absolute(
271
+ relative_pos=float(rel_value), det_width=width
272
+ )
267
273
  self._absolutePositionQLE.setText(str(abs_value))
268
274
 
269
275
  def _updateRelativePosition(self, width):
@@ -273,5 +279,7 @@ class _PositionInfoWidget(qt.QWidget):
273
279
  except Exception:
274
280
  return
275
281
  else:
276
- rel_value = float(abs_value) - (width - 1) / 2.0
282
+ rel_value = absolute_pos_to_relative(
283
+ absolute_pos=float(abs_value), det_width=width
284
+ )
277
285
  self._relativePositionQLE.setText(str(rel_value))