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,12 +1,16 @@
|
|
1
1
|
def relative_pos_to_absolute(relative_pos: float, det_width: int):
|
2
2
|
"""
|
3
|
-
|
3
|
+
Convert relative center of rotation to absolute.
|
4
4
|
"""
|
5
|
-
|
5
|
+
# Compute the midpoint differently for even and odd widths
|
6
|
+
midpoint = det_width / 2.0
|
7
|
+
return relative_pos + midpoint
|
6
8
|
|
7
9
|
|
8
10
|
def absolute_pos_to_relative(absolute_pos: float, det_width: int):
|
9
11
|
"""
|
10
|
-
|
12
|
+
Convert absolute center of rotation to relative.
|
11
13
|
"""
|
12
|
-
|
14
|
+
# Compute the midpoint differently for even and odd widths
|
15
|
+
midpoint = det_width / 2.0
|
16
|
+
return absolute_pos - midpoint
|
@@ -402,9 +402,7 @@ class TestNabuAndAxis(unittest.TestCase):
|
|
402
402
|
nabu_rotation_axis_value = self.get_saved_rotation_axis(nabu_conf_files[0])
|
403
403
|
self.assertEqual(
|
404
404
|
nabu_rotation_axis_value,
|
405
|
-
str(
|
406
|
-
self.scan._axis_params.relative_cor_value + (self.scan.dim_1 - 1) / 2.0
|
407
|
-
),
|
405
|
+
str(self.scan._axis_params.relative_cor_value + (self.scan.dim_1 / 2.0)),
|
408
406
|
)
|
409
407
|
|
410
408
|
|
tomwer/core/scan/scanbase.py
CHANGED
@@ -279,7 +279,6 @@ class TomwerScanBase(TomwerObject):
|
|
279
279
|
def get_opposite_projections(self, mode) -> tuple:
|
280
280
|
"""
|
281
281
|
Return the two best opposite projections for the required mode.
|
282
|
-
|
283
282
|
"""
|
284
283
|
radios_with_angle = self.projections_with_angle()
|
285
284
|
angles = numpy.array(
|
@@ -322,9 +321,10 @@ class TomwerScanBase(TomwerObject):
|
|
322
321
|
nearest_c1 = find_nearest(angles=angles, angle=couples[0])
|
323
322
|
nearest_c2 = find_nearest(angles=angles, angle=couples[1])
|
324
323
|
if nearest_c1 is not None and nearest_c2 is not None:
|
325
|
-
|
326
|
-
|
327
|
-
|
324
|
+
return (
|
325
|
+
AxisResource(radios_with_angle[nearest_c1], angle=nearest_c1),
|
326
|
+
AxisResource(radios_with_angle[nearest_c2], angle=nearest_c2),
|
327
|
+
)
|
328
328
|
else:
|
329
329
|
return None, None
|
330
330
|
|
@@ -40,24 +40,6 @@ class TestProcessRegistration(unittest.TestCase):
|
|
40
40
|
def tearDown(self):
|
41
41
|
shutil.rmtree(self.tmp_dir)
|
42
42
|
|
43
|
-
def testGetCorValues(self):
|
44
|
-
from tomwer.core.process.reconstruction.axis import AxisTask
|
45
|
-
|
46
|
-
for i in range(20):
|
47
|
-
results = {"center_of_rotation": i}
|
48
|
-
Task._register_process(
|
49
|
-
self.scan.process_file,
|
50
|
-
process=AxisTask,
|
51
|
-
entry=self.scan.entry,
|
52
|
-
configuration=None,
|
53
|
-
results=results,
|
54
|
-
process_index=i,
|
55
|
-
)
|
56
|
-
cor_value = AxisTask.get_cor_frm_process_file(
|
57
|
-
self.scan.process_file, entry=self.scan.entry
|
58
|
-
)
|
59
|
-
self.assertEqual(cor_value, 19)
|
60
|
-
|
61
43
|
def testGetProcessNodes(self):
|
62
44
|
"""insure it return the last dark process based on the processing index"""
|
63
45
|
|
tomwer/gui/fonts.py
ADDED
@@ -10,6 +10,10 @@ from silx.gui import qt
|
|
10
10
|
from tomwer.gui.utils.qt_utils import block_signals
|
11
11
|
|
12
12
|
from tomwer.core.process.reconstruction.axis.mode import AxisMode
|
13
|
+
from tomwer.core.process.reconstruction.utils.cor import (
|
14
|
+
absolute_pos_to_relative,
|
15
|
+
relative_pos_to_absolute,
|
16
|
+
)
|
13
17
|
from tomwer.core.scan.scanbase import TomwerScanBase
|
14
18
|
|
15
19
|
from ...utils.scandescription import ScanNameLabelAndShape
|
@@ -124,17 +128,15 @@ class AxisMainWindow(qt.QMainWindow):
|
|
124
128
|
)
|
125
129
|
|
126
130
|
def setAutoUpdateEstimatedCor(self, value):
|
127
|
-
self._axisWidget.
|
128
|
-
|
129
|
-
|
130
|
-
return self._axisWidget.updateAutomaticallyEstimatedCor()
|
131
|
+
self._axisWidget._settingsWidget._mainWidget.setUpdateXRotationAxisPixelPositionOnNewScan(
|
132
|
+
value
|
133
|
+
)
|
131
134
|
|
132
135
|
def manual_uses_full_image(self, value):
|
133
136
|
self._axisWidget.manual_uses_full_image(value)
|
134
137
|
|
135
138
|
def _modeChanged(self):
|
136
139
|
self.getAxisParams().mode = self.getMode()
|
137
|
-
self.getAxisParams().use_sinogram = self.useSinogram()
|
138
140
|
|
139
141
|
def _CORValueLocked(self, lock):
|
140
142
|
if lock:
|
@@ -152,8 +154,8 @@ class AxisMainWindow(qt.QMainWindow):
|
|
152
154
|
and relative_value is not None
|
153
155
|
):
|
154
156
|
try:
|
155
|
-
absolute_value = (
|
156
|
-
relative_value
|
157
|
+
absolute_value = relative_pos_to_absolute(
|
158
|
+
relative_pos=relative_value, det_width=self._axis_params.frame_width
|
157
159
|
)
|
158
160
|
except TypeError:
|
159
161
|
absolute_value = None
|
@@ -163,8 +165,8 @@ class AxisMainWindow(qt.QMainWindow):
|
|
163
165
|
and absolute_value is not None
|
164
166
|
):
|
165
167
|
try:
|
166
|
-
relative_value = (
|
167
|
-
absolute_value
|
168
|
+
relative_value = absolute_pos_to_relative(
|
169
|
+
absolute_pos=absolute_value, det_width=self._axis_params.frame_width
|
168
170
|
)
|
169
171
|
except TypeError:
|
170
172
|
relative_value = None
|
@@ -262,3 +264,12 @@ class AxisMainWindow(qt.QMainWindow):
|
|
262
264
|
|
263
265
|
def setEstimatedCor(self, value: float):
|
264
266
|
self._axisWidget.setEstimatedCor(value=value)
|
267
|
+
|
268
|
+
def getAutoUpdateEstimatedCor(self):
|
269
|
+
return self._axisWidget.updateXRotationAxisPixelPositionOnNewScan()
|
270
|
+
|
271
|
+
def isYAxisInverted(self) -> bool:
|
272
|
+
return self._axisWidget.isYAxisInverted()
|
273
|
+
|
274
|
+
def setYAxisInverted(self, checked: bool):
|
275
|
+
return self._axisWidget.setYAxisInverted(checked=checked)
|
@@ -2,9 +2,17 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from silx.gui import qt
|
4
4
|
|
5
|
+
from tomwer.core.process.reconstruction.axis.mode import AxisMode, AXIS_MODE_METADATAS
|
5
6
|
from tomwer.synctools.axis import QAxisRP
|
6
7
|
from tomwer.core.process.reconstruction.axis.params import AxisCalculationInput
|
7
8
|
from tomwer.gui.utils.qt_utils import block_signals
|
9
|
+
from tomwer.gui.utils.scrollarea import QComboBoxIgnoreWheel, QSpinBoxIgnoreWheel
|
10
|
+
from tomwer.core.process.reconstruction.axis.params import (
|
11
|
+
DEFAULT_CMP_N_SUBSAMPLING_Y,
|
12
|
+
DEFAULT_CMP_OVERSAMPLING,
|
13
|
+
DEFAULT_CMP_TAKE_LOG,
|
14
|
+
DEFAULT_CMP_THETA,
|
15
|
+
)
|
8
16
|
|
9
17
|
|
10
18
|
class AxisOptionsWidget(qt.QWidget):
|
@@ -14,12 +22,50 @@ class AxisOptionsWidget(qt.QWidget):
|
|
14
22
|
Used as a tab of the AxisSettingsTabWidget
|
15
23
|
"""
|
16
24
|
|
25
|
+
sigChanged = qt.Signal()
|
26
|
+
"""Emit when the options changed"""
|
27
|
+
|
17
28
|
def __init__(self, parent, axis_params):
|
18
29
|
qt.QWidget.__init__(self, parent=parent)
|
19
30
|
assert isinstance(axis_params, QAxisRP)
|
20
31
|
self._axis_params = axis_params
|
21
32
|
self.setLayout(qt.QVBoxLayout())
|
22
33
|
|
34
|
+
# cor_options
|
35
|
+
self._corOptsWidget = qt.QWidget(self)
|
36
|
+
self._corOptsWidget.setLayout(qt.QFormLayout())
|
37
|
+
self._corOpts = qt.QLineEdit(self)
|
38
|
+
self._corOpts.setToolTip(
|
39
|
+
"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 (;)."
|
40
|
+
)
|
41
|
+
self._corOpts.setPlaceholderText("low_pass=1; high_pass=20")
|
42
|
+
self._corOptsWidget.layout().addRow("cor advanced options", self._corOpts)
|
43
|
+
self.layout().addWidget(self._corOptsWidget)
|
44
|
+
|
45
|
+
# padding option
|
46
|
+
self._padding_widget = qt.QGroupBox("padding mode")
|
47
|
+
self._padding_widget.setCheckable(True)
|
48
|
+
self.layout().addWidget(self._padding_widget)
|
49
|
+
self._padding_widget.setLayout(qt.QHBoxLayout())
|
50
|
+
|
51
|
+
self._qbPaddingMode = QComboBoxIgnoreWheel(self._padding_widget)
|
52
|
+
for _mode in (
|
53
|
+
"constant",
|
54
|
+
"edge",
|
55
|
+
"linear_ramp",
|
56
|
+
"maximum",
|
57
|
+
"mean",
|
58
|
+
"median",
|
59
|
+
"minimum",
|
60
|
+
"reflect",
|
61
|
+
"symmetric",
|
62
|
+
"wrap",
|
63
|
+
):
|
64
|
+
self._qbPaddingMode.addItem(_mode)
|
65
|
+
def_index = self._qbPaddingMode.findText("edge")
|
66
|
+
self._qbPaddingMode.setCurrentIndex(def_index)
|
67
|
+
self._padding_widget.layout().addWidget(self._qbPaddingMode)
|
68
|
+
|
23
69
|
# define common options
|
24
70
|
self._commonOpts = qt.QWidget(parent=self)
|
25
71
|
self._commonOpts.setLayout(qt.QFormLayout())
|
@@ -30,39 +76,75 @@ class AxisOptionsWidget(qt.QWidget):
|
|
30
76
|
self._qcbDataMode.addItem(data_mode.name(), data_mode)
|
31
77
|
self._qcbDataMode.hide()
|
32
78
|
|
33
|
-
# add scale option
|
34
|
-
self._scaleOpt = qt.QCheckBox(parent=self)
|
35
|
-
self._commonOpts.layout().addRow("scale the two images", self._scaleOpt)
|
36
79
|
self.layout().addWidget(self._commonOpts)
|
37
80
|
|
38
|
-
#
|
39
|
-
self.
|
40
|
-
|
81
|
+
# composite method advanced options
|
82
|
+
self._compositeOptsGroup = CompositeOptsGroup(
|
83
|
+
parent=self, axis_params=axis_params
|
84
|
+
)
|
85
|
+
self.layout().addWidget(self._compositeOptsGroup)
|
41
86
|
|
42
87
|
# set up
|
43
88
|
self.setCalculationInputType(self._axis_params.calculation_input_type)
|
89
|
+
self._compositeOptsGroup.setVisible(
|
90
|
+
self._axis_params.mode in (AxisMode.near, AxisMode.composite_coarse_to_fine)
|
91
|
+
)
|
44
92
|
|
45
93
|
# connect signal / slot
|
46
|
-
self.
|
94
|
+
self._corOpts.editingFinished.connect(self._updateAdvancedCorOptions)
|
47
95
|
self._qcbDataMode.currentIndexChanged.connect(self._updateInputType)
|
48
|
-
self._axis_params.sigChanged.connect(self.
|
96
|
+
self._axis_params.sigChanged.connect(self._axis_params_changed)
|
97
|
+
self._qbPaddingMode.currentIndexChanged.connect(self._paddingModeChanged)
|
98
|
+
self._padding_widget.toggled.connect(self._paddingModeChanged)
|
99
|
+
self._compositeOptsGroup.sigChanged.connect(self.sigChanged)
|
49
100
|
|
50
|
-
def
|
101
|
+
def _axis_params_changed(self):
|
51
102
|
with block_signals(self):
|
103
|
+
# update according to AxisCalculationInput
|
52
104
|
index = self._qcbDataMode.findText(
|
53
105
|
self._axis_params.calculation_input_type.name()
|
54
106
|
)
|
55
107
|
if index >= 0:
|
56
108
|
self._qcbDataMode.setCurrentIndex(index)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
109
|
+
# update advanced cor options visibility (not relevant if mode is manual or read)
|
110
|
+
axis_mode = self._axis_params.mode
|
111
|
+
self._corOptsWidget.setVisible(
|
112
|
+
axis_mode
|
113
|
+
not in (
|
114
|
+
AxisMode.manual,
|
115
|
+
AxisMode.read,
|
116
|
+
)
|
117
|
+
)
|
118
|
+
# update cor options value
|
119
|
+
self.setCorOptions(self._axis_params.extra_cor_options)
|
120
|
+
self.setPaddingMode(self._axis_params.padding_mode)
|
121
|
+
self._padding_widget.setVisible(
|
122
|
+
AXIS_MODE_METADATAS[axis_mode].allows_padding
|
123
|
+
)
|
63
124
|
|
64
125
|
def _updateInputType(self, *arg, **kwargs):
|
65
126
|
self._axis_params.calculation_input_type = self.getCalculationInputType()
|
127
|
+
self.sigChanged.emit()
|
128
|
+
|
129
|
+
def _paddingModeChanged(self, *args, **kwargs):
|
130
|
+
self._axis_params.padding_mode = self.getPaddingMode()
|
131
|
+
self.sigChanged.emit()
|
132
|
+
|
133
|
+
def getPaddingMode(self):
|
134
|
+
if self._padding_widget.isChecked():
|
135
|
+
return self._qbPaddingMode.currentText()
|
136
|
+
else:
|
137
|
+
return None
|
138
|
+
|
139
|
+
def setPaddingMode(self, mode):
|
140
|
+
index = self._qbPaddingMode.findText(mode)
|
141
|
+
if index >= 0:
|
142
|
+
self._qbPaddingMode.setCurrentIndex(index)
|
143
|
+
self._paddingModeChanged()
|
144
|
+
|
145
|
+
def _updateAdvancedCorOptions(self, *args, **kwargs):
|
146
|
+
self._axis_params.extra_cor_options = self.getCorOptions()
|
147
|
+
self.sigChanged.emit()
|
66
148
|
|
67
149
|
def getCalculationInputType(self, *arg, **kwargs):
|
68
150
|
return AxisCalculationInput.from_value(self._qcbDataMode.currentText())
|
@@ -73,81 +155,159 @@ class AxisOptionsWidget(qt.QWidget):
|
|
73
155
|
self._qcbDataMode.setCurrentIndex(index_dm)
|
74
156
|
|
75
157
|
def setAxisParams(self, axis):
|
76
|
-
self._nearOpts.setAxisParams(axis=axis)
|
77
158
|
self._axis_params = axis
|
78
159
|
with block_signals(self):
|
79
|
-
self._scaleOpt.setChecked(axis.scale_img2_to_img1)
|
80
160
|
index = self._qcbDataMode.findText(axis.calculation_input_type.name())
|
81
161
|
self._qcbDataMode.setCurrentIndex(index)
|
162
|
+
self._compositeOptsGroup.setAxisParams(axis)
|
82
163
|
|
164
|
+
def getCorOptions(self):
|
165
|
+
return self._corOpts.text()
|
83
166
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
self._axis_params = axis_params
|
91
|
-
|
92
|
-
self.setLayout(qt.QFormLayout())
|
93
|
-
|
94
|
-
self._stdMaxOpt = qt.QCheckBox(parent=self)
|
95
|
-
self.layout().addRow("look at max standard deviation", self._stdMaxOpt)
|
96
|
-
|
97
|
-
self._nearWX = qt.QSpinBox(parent=self)
|
98
|
-
self._nearWX.setMinimum(1)
|
99
|
-
self._nearWX.setValue(5)
|
100
|
-
self.layout().addRow("window size", self._nearWX)
|
101
|
-
|
102
|
-
self._fineStepX = qt.QDoubleSpinBox(parent=self)
|
103
|
-
self._fineStepX.setMinimum(0.05)
|
104
|
-
self._fineStepX.setSingleStep(0.05)
|
105
|
-
self._fineStepX.setMaximum(1.0)
|
106
|
-
self.layout().addRow("fine step x", self._fineStepX)
|
107
|
-
|
108
|
-
# connect signal / Slot
|
109
|
-
self._stdMaxOpt.toggled.connect(self._lookForStxMaxChanged)
|
110
|
-
self._nearWX.valueChanged.connect(self._windowSizeChanged)
|
111
|
-
self._fineStepX.valueChanged.connect(self._fineStepXChanged)
|
112
|
-
|
113
|
-
def _lookForStxMaxChanged(self, *args, **kwargs):
|
114
|
-
self._axis_params.look_at_stdmax = self.isLookAtStdMax()
|
115
|
-
|
116
|
-
def isLookAtStdMax(self) -> bool:
|
117
|
-
"""
|
167
|
+
def setCorOptions(self, opts: str | None):
|
168
|
+
with block_signals(self._axis_params):
|
169
|
+
self._corOpts.clear()
|
170
|
+
if opts:
|
171
|
+
self._corOpts.setText(opts)
|
172
|
+
self._updateAdvancedCorOptions()
|
118
173
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
174
|
+
def setMode(self, mode: AxisMode):
|
175
|
+
composite_opts_visible = AxisMode.from_value(mode) in (
|
176
|
+
AxisMode.composite_coarse_to_fine,
|
177
|
+
AxisMode.near,
|
178
|
+
)
|
179
|
+
self._compositeOptsGroup.setVisible(composite_opts_visible)
|
123
180
|
|
124
|
-
def _windowSizeChanged(self, *args, **kwargs):
|
125
|
-
self._axis_params.near_wx = self.getWindowSize()
|
126
181
|
|
127
|
-
|
128
|
-
|
182
|
+
class CompositeOptsGroup(qt.QGroupBox):
|
183
|
+
"""Group box dedicated to the composite algorithms"""
|
129
184
|
|
130
|
-
|
131
|
-
|
132
|
-
return self._nearWX.value()
|
185
|
+
sigChanged = qt.Signal()
|
186
|
+
"""Emit when the options changed"""
|
133
187
|
|
134
|
-
def
|
135
|
-
self._axis_params
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
188
|
+
def __init__(self, title="composite options", parent=None, axis_params=None):
|
189
|
+
self._axis_params = axis_params
|
190
|
+
super().__init__(title, parent)
|
191
|
+
## options for the composite mode
|
192
|
+
self.setLayout(qt.QFormLayout())
|
193
|
+
self.layout().setContentsMargins(0, 0, 0, 0)
|
194
|
+
|
195
|
+
self._thetaSB = QSpinBoxIgnoreWheel(self)
|
196
|
+
self._thetaSB.setRange(0, 360)
|
197
|
+
self._thetaSB.setValue(DEFAULT_CMP_THETA)
|
198
|
+
self._thetaSB.setToolTip("a radio will be picked each theta degrees")
|
199
|
+
self._thetaLabel = qt.QLabel("angle interval (in degree)", self)
|
200
|
+
self._thetaLabel.setToolTip(
|
201
|
+
"algorithm will take one projection each 'angle interval'. Also know as 'theta'"
|
202
|
+
)
|
203
|
+
self.layout().addRow(self._thetaLabel, self._thetaSB)
|
204
|
+
|
205
|
+
self._oversamplingSB = QSpinBoxIgnoreWheel(self)
|
206
|
+
self._oversamplingSB.setRange(1, 999999)
|
207
|
+
self._oversamplingSB.setValue(DEFAULT_CMP_OVERSAMPLING)
|
208
|
+
self._oversamplingSB.setToolTip("sinogram oversampling")
|
209
|
+
self.layout().addRow("oversampling", self._oversamplingSB)
|
210
|
+
|
211
|
+
self._nearWidthSB = QSpinBoxIgnoreWheel(self)
|
212
|
+
self._nearWidthSB.setRange(1, 999999)
|
213
|
+
self._nearWidthSB.setValue(0)
|
214
|
+
self._nearWidthSB.setToolTip("position to be used with near option")
|
215
|
+
self._nearWidthLabel = qt.QLabel("near width", self)
|
216
|
+
self._nearWidthLabel.setToolTip("position to be used with near option")
|
217
|
+
self.layout().addRow(self._nearWidthLabel, self._nearWidthSB)
|
218
|
+
|
219
|
+
self._subsamplingYSB = QSpinBoxIgnoreWheel(self)
|
220
|
+
self._subsamplingYSB.setRange(1, 999999)
|
221
|
+
self._subsamplingYSB.setValue(DEFAULT_CMP_N_SUBSAMPLING_Y)
|
222
|
+
self._subsamplingYSB.setToolTip("sinogram number of subsampling along y")
|
223
|
+
self.layout().addRow("n_subsampling_y", self._subsamplingYSB)
|
224
|
+
|
225
|
+
self._takeLogCB = qt.QCheckBox(self)
|
226
|
+
self._takeLogCB.setToolTip("Take logarithm")
|
227
|
+
self._takeLogCB.setChecked(DEFAULT_CMP_TAKE_LOG)
|
228
|
+
self._takeTheLogLabel = qt.QLabel("linearisation (-log(I/I0))")
|
229
|
+
self._takeTheLogLabel.setToolTip(
|
230
|
+
"take (-log(I/I0)) as input. Also know as 'take_log' option"
|
231
|
+
)
|
232
|
+
self.layout().addRow(self._takeTheLogLabel, self._takeLogCB)
|
143
233
|
|
144
|
-
|
145
|
-
|
234
|
+
# connect signal / slot
|
235
|
+
self._thetaSB.valueChanged.connect(self._changed)
|
236
|
+
self._oversamplingSB.valueChanged.connect(self._changed)
|
237
|
+
self._subsamplingYSB.valueChanged.connect(self._changed)
|
238
|
+
self._nearWidthSB.valueChanged.connect(self._changed)
|
239
|
+
self._takeLogCB.toggled.connect(self._changed)
|
146
240
|
|
147
|
-
|
148
|
-
"""
|
241
|
+
def setAxisParams(self, axis_params):
|
149
242
|
with block_signals(self):
|
150
|
-
self.
|
151
|
-
|
152
|
-
|
153
|
-
|
243
|
+
self.setConfiguration(axis_params.composite_options)
|
244
|
+
self._axis_params = axis_params
|
245
|
+
|
246
|
+
def _changed(self):
|
247
|
+
if self._axis_params is not None:
|
248
|
+
self._axis_params.composite_options = self.getConfiguration()
|
249
|
+
self.sigChanged.emit()
|
250
|
+
|
251
|
+
def getTheta(self) -> int:
|
252
|
+
return self._thetaSB.value()
|
253
|
+
|
254
|
+
def setTheta(self, theta: int) -> None:
|
255
|
+
self._thetaSB.setValue(theta)
|
256
|
+
|
257
|
+
def getOversampling(self) -> int:
|
258
|
+
return self._oversamplingSB.value()
|
259
|
+
|
260
|
+
def setOversampling(self, oversampling: int) -> None:
|
261
|
+
self._oversamplingSB.setValue(oversampling)
|
262
|
+
|
263
|
+
def getNearWidth(self) -> int:
|
264
|
+
return self._nearWidthSB.value()
|
265
|
+
|
266
|
+
def setNearWidth(self, value) -> int:
|
267
|
+
return self._nearWidthSB.setValue(value)
|
268
|
+
|
269
|
+
def getSubSamplingY(self) -> int:
|
270
|
+
return self._subsamplingYSB.value()
|
271
|
+
|
272
|
+
def setSubSamplingY(self, subsampling: int) -> None:
|
273
|
+
self._subsamplingYSB.setValue(subsampling)
|
274
|
+
|
275
|
+
def getTakeLog(self) -> bool:
|
276
|
+
return self._takeLogCB.isChecked()
|
277
|
+
|
278
|
+
def setTakeLog(self, log: bool) -> None:
|
279
|
+
self._takeLogCB.setChecked(log)
|
280
|
+
|
281
|
+
def getConfiguration(self) -> dict:
|
282
|
+
|
283
|
+
return {
|
284
|
+
"theta": self.getTheta(),
|
285
|
+
"oversampling": self.getOversampling(),
|
286
|
+
"n_subsampling_y": self.getSubSamplingY(),
|
287
|
+
"take_log": self.getTakeLog(),
|
288
|
+
"near_width": self.getNearWidth(),
|
289
|
+
}
|
290
|
+
|
291
|
+
def setConfiguration(self, opts: dict) -> None:
|
292
|
+
if not isinstance(opts, dict):
|
293
|
+
raise TypeError("opts should be an instance of dict")
|
294
|
+
# theta
|
295
|
+
theta = opts.get("theta", None)
|
296
|
+
if theta is not None:
|
297
|
+
self.setTheta(theta=theta)
|
298
|
+
# oversampling
|
299
|
+
oversampling = opts.get("oversampling", None)
|
300
|
+
if oversampling is not None:
|
301
|
+
self.setOversampling(oversampling)
|
302
|
+
# n subsampling y
|
303
|
+
n_subsampling_y = opts.get("n_subsampling_y", None)
|
304
|
+
if n_subsampling_y is not None:
|
305
|
+
self.setSubSamplingY(n_subsampling_y)
|
306
|
+
# near_width
|
307
|
+
near_width = opts.get("near_width", None)
|
308
|
+
if near_width is not None:
|
309
|
+
self.setNearWidth(near_width)
|
310
|
+
# take log
|
311
|
+
take_log = opts.get("take_log", None)
|
312
|
+
if take_log is not None:
|
313
|
+
self.setTakeLog(take_log)
|
@@ -152,6 +152,7 @@ class AxisSettingsWidget(qt.QWidget):
|
|
152
152
|
with block_signals(self._axisParams):
|
153
153
|
self._axisParams.mode = mode
|
154
154
|
self._mainWidget._calculationWidget.setMode(mode)
|
155
|
+
self._mainWidget._optionsWidget.setMode(mode)
|
155
156
|
self._updateAxisView()
|
156
157
|
self._axisParams.sigChanged.emit()
|
157
158
|
|
@@ -230,15 +231,15 @@ class AxisSettingsWidget(qt.QWidget):
|
|
230
231
|
|
231
232
|
# expose API
|
232
233
|
|
233
|
-
def updateAutomaticallyEstimatedCor(self):
|
234
|
-
return self._mainWidget.updateAutomaticallyEstimatedCor()
|
235
|
-
|
236
|
-
def setUpdateAutomaticallyEstimatedCor(self, value):
|
237
|
-
self._mainWidget.setUpdateAutomaticallyEstimatedCor(value)
|
238
|
-
|
239
234
|
def setEstimatedCor(self, value):
|
240
235
|
self._mainWidget.setEstimatedCorValue(value=value)
|
241
236
|
|
237
|
+
def updateXRotationAxisPixelPositionOnNewScan(self) -> bool:
|
238
|
+
return self._mainWidget.updateXRotationAxisPixelPositionOnNewScan()
|
239
|
+
|
240
|
+
def setUpdateXRotationAxisPixelPositionOnNewScan(self, update: bool):
|
241
|
+
self._mainWidget.setUpdateXRotationAxisPixelPositionOnNewScan(update=update)
|
242
|
+
|
242
243
|
def getEstimatedCor(self):
|
243
244
|
return self._mainWidget.getEstimatedCor()
|
244
245
|
|
@@ -266,6 +267,12 @@ class AxisSettingsWidget(qt.QWidget):
|
|
266
267
|
def setModeLock(self, mode):
|
267
268
|
return self._mainWidget.setModeLock(mode=mode)
|
268
269
|
|
270
|
+
def isYAxisInverted(self) -> bool:
|
271
|
+
return self._mainWidget.isYAxisInverted()
|
272
|
+
|
273
|
+
def setYAxisInverted(self, checked: bool):
|
274
|
+
return self._mainWidget.setYAxisInverted(checked=checked)
|
275
|
+
|
269
276
|
|
270
277
|
class ManualAxisSelectionWidget(qt.QWidget):
|
271
278
|
sigResetZoomRequested = qt.Signal()
|
@@ -492,6 +499,8 @@ class AxisSettingsTabWidget(qt.QTabWidget):
|
|
492
499
|
sigModeChanged = qt.Signal(str)
|
493
500
|
"""Signal emit when mode (algorithm) is changed"""
|
494
501
|
|
502
|
+
sigUpdateXRotAxisPixelPosOnNewScan = qt.Signal()
|
503
|
+
|
495
504
|
def __init__(
|
496
505
|
self,
|
497
506
|
recons_params: QAxisRP | None,
|
@@ -517,12 +526,9 @@ class AxisSettingsTabWidget(qt.QTabWidget):
|
|
517
526
|
spacer.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding)
|
518
527
|
widget.layout().addWidget(spacer)
|
519
528
|
|
520
|
-
self._optionsSA = qt.QScrollArea(parent=self)
|
521
|
-
self._optionsSA.setWidget(self._optionsWidget)
|
522
529
|
self.addTab(self._calculationWidget, "calculation")
|
523
|
-
self.addTab(self.
|
530
|
+
self.addTab(self._optionsWidget, "options")
|
524
531
|
# simplify set up. Hide options
|
525
|
-
self._optionsSA.hide()
|
526
532
|
self.addTab(self._inputWidget, "input")
|
527
533
|
|
528
534
|
# set up
|
@@ -532,11 +538,11 @@ class AxisSettingsTabWidget(qt.QTabWidget):
|
|
532
538
|
# connect signal / slot
|
533
539
|
self._calculationWidget.sigLockModeChanged.connect(self.sigLockModeChanged)
|
534
540
|
self.sigModeChanged.connect(self._updatePossibleInput)
|
541
|
+
self.sigModeChanged.connect(self._updatePossibleOptions)
|
535
542
|
self._inputWidget._sigUrlChanged.connect(self._urlChanged)
|
536
543
|
self._calculationWidget.sigModeChanged.connect(self.sigModeChanged)
|
537
|
-
|
538
|
-
|
539
|
-
self._inputWidget._changed
|
544
|
+
self._calculationWidget.sigUpdateXRotAxisPixelPosOnNewScan.connect(
|
545
|
+
self.sigUpdateXRotAxisPixelPosOnNewScan
|
540
546
|
)
|
541
547
|
|
542
548
|
def _urlChanged(self):
|
@@ -544,6 +550,7 @@ class AxisSettingsTabWidget(qt.QTabWidget):
|
|
544
550
|
|
545
551
|
def setScan(self, scan):
|
546
552
|
if scan is not None:
|
553
|
+
self._calculationWidget.setScan(scan)
|
547
554
|
self._inputWidget.setScan(scan)
|
548
555
|
|
549
556
|
def setAxisParams(self, axis):
|
@@ -562,6 +569,10 @@ class AxisSettingsTabWidget(qt.QTabWidget):
|
|
562
569
|
self._inputWidget.setEnabled(True)
|
563
570
|
self._inputWidget.setValidInputs(valid_inputs)
|
564
571
|
|
572
|
+
def _updatePossibleOptions(self):
|
573
|
+
mode = self.getMode()
|
574
|
+
self._optionsWidget.setMode(mode)
|
575
|
+
|
565
576
|
# expose API
|
566
577
|
def isModeLock(self) -> bool:
|
567
578
|
return self._calculationWidget.isModeLock()
|
@@ -575,15 +586,25 @@ class AxisSettingsTabWidget(qt.QTabWidget):
|
|
575
586
|
def getEstimatedCor(self):
|
576
587
|
return self._calculationWidget.getEstimatedCor()
|
577
588
|
|
589
|
+
def updateXRotationAxisPixelPositionOnNewScan(self) -> bool:
|
590
|
+
return self._calculationWidget.updateXRotationAxisPixelPositionOnNewScan()
|
591
|
+
|
592
|
+
def setUpdateXRotationAxisPixelPositionOnNewScan(self, update: bool):
|
593
|
+
self._calculationWidget.setUpdateXRotationAxisPixelPositionOnNewScan(
|
594
|
+
update=update
|
595
|
+
)
|
596
|
+
|
578
597
|
def getMode(self):
|
579
598
|
"""Return algorithm to use for axis calculation"""
|
580
599
|
return self._calculationWidget.getMode()
|
581
600
|
|
582
|
-
def
|
583
|
-
return self._calculationWidget.
|
601
|
+
def isYAxisInverted(self) -> bool:
|
602
|
+
return self._calculationWidget._estimatedCorWidget.isYAxisInverted()
|
584
603
|
|
585
|
-
def
|
586
|
-
self._calculationWidget.
|
604
|
+
def setYAxisInverted(self, checked: bool):
|
605
|
+
return self._calculationWidget._estimatedCorWidget.setYAxisInverted(
|
606
|
+
checked=checked
|
607
|
+
)
|
587
608
|
|
588
609
|
|
589
610
|
class _ShiftControl(qt.QWidget):
|