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
@@ -6,17 +6,10 @@ from silx.gui import qt
|
|
6
6
|
|
7
7
|
from tomwer.core.process.reconstruction.axis import mode as axis_mode
|
8
8
|
from tomwer.core.process.reconstruction.axis.anglemode import CorAngleMode
|
9
|
-
from tomwer.core.process.reconstruction.axis.params import (
|
10
|
-
DEFAULT_CMP_N_SUBSAMPLING_Y,
|
11
|
-
DEFAULT_CMP_OVERSAMPLING,
|
12
|
-
DEFAULT_CMP_TAKE_LOG,
|
13
|
-
DEFAULT_CMP_THETA,
|
14
|
-
)
|
15
9
|
from tomwer.gui.utils.scrollarea import QComboBoxIgnoreWheel
|
16
10
|
from tomwer.core.scan.scanbase import TomwerScanBase
|
17
11
|
from tomwer.gui.utils.qt_utils import block_signals
|
18
12
|
from tomwer.synctools.axis import QAxisRP
|
19
|
-
from .CalculationWidget import CalculationWidget
|
20
13
|
from .ManualFramesSelection import ManualFramesSelection
|
21
14
|
|
22
15
|
_logger = logging.getLogger(__name__)
|
@@ -55,74 +48,24 @@ class InputWidget(qt.QWidget):
|
|
55
48
|
|
56
49
|
# sinogram input
|
57
50
|
self._sinogramGB = qt.QGroupBox(self)
|
58
|
-
self._sinogramGB.setLayout(qt.
|
59
|
-
self._standardSinogramOpts = qt.QGroupBox(self)
|
60
|
-
self._sinogramGB.layout().addWidget(self._standardSinogramOpts)
|
61
|
-
self._standardSinogramOpts.setLayout(qt.QFormLayout())
|
62
|
-
self._standardSinogramOpts.layout().setContentsMargins(0, 0, 0, 0)
|
63
|
-
self._standardSinogramOpts.setTitle("standard options")
|
51
|
+
self._sinogramGB.setLayout(qt.QFormLayout())
|
64
52
|
|
65
53
|
self._sinogramGB.setTitle("sinogram")
|
66
54
|
self._sinogramGB.setCheckable(True)
|
67
55
|
self.layout().addWidget(self._sinogramGB)
|
68
56
|
## sinogram line
|
69
57
|
self._sinogramLineSB = _SliceSelector(self)
|
70
|
-
self.
|
58
|
+
self._sinogramGB.layout().addRow("line", self._sinogramLineSB)
|
71
59
|
## sinogram subsampling
|
72
60
|
self._sinogramSubsampling = qt.QSpinBox(self)
|
73
61
|
self._sinogramSubsampling.setRange(1, 1000)
|
74
62
|
self._sinogramSubsampling.setValue(10)
|
75
|
-
self.
|
76
|
-
"subsampling", self._sinogramSubsampling
|
77
|
-
)
|
63
|
+
self._sinogramGB.layout().addRow("subsampling", self._sinogramSubsampling)
|
78
64
|
|
79
65
|
self._spacer = qt.QWidget(self)
|
80
66
|
self._spacer.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding)
|
81
67
|
self.layout().addWidget(self._spacer)
|
82
68
|
|
83
|
-
## options for the composite mode
|
84
|
-
self._compositeOpts = qt.QGroupBox(self)
|
85
|
-
self._compositeOpts.setTitle("composite options")
|
86
|
-
self._sinogramGB.layout().addWidget(self._compositeOpts)
|
87
|
-
self._compositeOpts.setLayout(qt.QFormLayout())
|
88
|
-
self._compositeOpts.layout().setContentsMargins(0, 0, 0, 0)
|
89
|
-
self._thetaSB = qt.QSpinBox(self)
|
90
|
-
self._thetaSB.setRange(0, 360)
|
91
|
-
self._thetaSB.setValue(DEFAULT_CMP_THETA)
|
92
|
-
self._thetaSB.setToolTip("a radio will be picked each theta degres")
|
93
|
-
self._thetaLabel = qt.QLabel("angle interval (in degree)", self)
|
94
|
-
self._thetaLabel.setToolTip(
|
95
|
-
"algorithm will take one projection each 'angle interval'. Also know as 'theta'"
|
96
|
-
)
|
97
|
-
self._compositeOpts.layout().addRow(self._thetaLabel, self._thetaSB)
|
98
|
-
|
99
|
-
self._oversamplingSB = qt.QSpinBox(self)
|
100
|
-
self._oversamplingSB.setValue(DEFAULT_CMP_OVERSAMPLING)
|
101
|
-
self._oversamplingSB.setToolTip("sinogram oversampling")
|
102
|
-
self._compositeOpts.layout().addRow("oversampling", self._oversamplingSB)
|
103
|
-
|
104
|
-
self._nearwidthSB = qt.QSpinBox(self)
|
105
|
-
self._nearwidthSB.setRange(-40000, 40000)
|
106
|
-
self._nearwidthSB.setValue(0)
|
107
|
-
self._nearwidthSB.setToolTip("position to be used with near option")
|
108
|
-
self._nearwidthLabel = qt.QLabel("near width", self)
|
109
|
-
self._nearwidthLabel.setToolTip("position to be used with near option")
|
110
|
-
self._compositeOpts.layout().addRow(self._nearwidthLabel, self._nearwidthSB)
|
111
|
-
|
112
|
-
self._subsamplingYSB = qt.QSpinBox(self)
|
113
|
-
self._subsamplingYSB.setValue(DEFAULT_CMP_N_SUBSAMPLING_Y)
|
114
|
-
self._subsamplingYSB.setToolTip("sinogram number of subsampling along y")
|
115
|
-
self._compositeOpts.layout().addRow("n_subsampling_y", self._subsamplingYSB)
|
116
|
-
|
117
|
-
self._takeLogCB = qt.QCheckBox(self)
|
118
|
-
self._takeLogCB.setToolTip("Take logarithm")
|
119
|
-
self._takeLogCB.setChecked(DEFAULT_CMP_TAKE_LOG)
|
120
|
-
self._takeTheLogLabel = qt.QLabel("linearisation (-log(I/I0))")
|
121
|
-
self._takeTheLogLabel.setToolTip(
|
122
|
-
"take (-log(I/I0)) as input. Also know as 'take_log' option"
|
123
|
-
)
|
124
|
-
self._compositeOpts.layout().addRow(self._takeTheLogLabel, self._takeLogCB)
|
125
|
-
|
126
69
|
# set up
|
127
70
|
self._sinogramGB.setChecked(False)
|
128
71
|
|
@@ -131,11 +74,6 @@ class InputWidget(qt.QWidget):
|
|
131
74
|
self._radioGB.toggled.connect(self._radiosChecked)
|
132
75
|
self._sinogramSubsampling.valueChanged.connect(self._changed)
|
133
76
|
self._sinogramLineSB.sigChanged.connect(self._changed)
|
134
|
-
self._thetaSB.valueChanged.connect(self._changed)
|
135
|
-
self._oversamplingSB.valueChanged.connect(self._changed)
|
136
|
-
self._subsamplingYSB.valueChanged.connect(self._changed)
|
137
|
-
self._nearwidthSB.valueChanged.connect(self._changed)
|
138
|
-
self._takeLogCB.toggled.connect(self._changed)
|
139
77
|
self._angleModeWidget.sigChanged.connect(self._sigUrlChanged)
|
140
78
|
|
141
79
|
def setScan(self, scan: TomwerScanBase):
|
@@ -151,10 +89,11 @@ class InputWidget(qt.QWidget):
|
|
151
89
|
if axis_params is not None:
|
152
90
|
assert isinstance(axis_params, QAxisRP)
|
153
91
|
with block_signals(self._sinogramGB):
|
154
|
-
self._sinogramChecked(
|
92
|
+
self._sinogramChecked(
|
93
|
+
axis_params.mode.requires_sinogram_index(), on_load=True
|
94
|
+
)
|
155
95
|
self._sinogramLineSB.setSlice(axis_params.sinogram_line)
|
156
96
|
self._sinogramSubsampling.setValue(axis_params.sinogram_subsampling)
|
157
|
-
self.setCompositeOptions(axis_params.composite_options)
|
158
97
|
self._angleModeWidget.setAxisParams(axis_params)
|
159
98
|
self._axis_params = axis_params
|
160
99
|
|
@@ -202,10 +141,8 @@ class InputWidget(qt.QWidget):
|
|
202
141
|
|
203
142
|
def _updateAxisParams(self):
|
204
143
|
if not self._blockUpdateAxisParams:
|
205
|
-
self._axis_params.use_sinogram = self._sinogramGB.isChecked()
|
206
144
|
self._axis_params.sinogram_line = self.getSinogramLine()
|
207
145
|
self._axis_params.sinogram_subsampling = self.getSinogramSubsampling()
|
208
|
-
self._axis_params.composite_options = self.getCompositeOptions()
|
209
146
|
|
210
147
|
def setValidInputs(self, modes: list | tuple):
|
211
148
|
"""
|
@@ -230,102 +167,21 @@ class InputWidget(qt.QWidget):
|
|
230
167
|
raise ValueError("modes is empty")
|
231
168
|
else:
|
232
169
|
mode = axis_mode._InputType.from_value(modes.pop())
|
233
|
-
if mode
|
170
|
+
if mode is axis_mode._InputType.SINOGRAM:
|
234
171
|
self._sinogramGB.setEnabled(True)
|
235
172
|
self._radioGB.setEnabled(False)
|
236
173
|
self._sinogramGB.setChecked(True)
|
237
|
-
self._compositeOpts.setEnabled(mode is axis_mode._InputType.COMPOSITE)
|
238
|
-
self._standardSinogramOpts.setEnabled(
|
239
|
-
mode is not axis_mode._InputType.COMPOSITE
|
240
|
-
)
|
241
174
|
elif mode is axis_mode._InputType.RADIOS_X2:
|
242
175
|
self._radioGB.setEnabled(True)
|
243
176
|
self._sinogramGB.setEnabled(False)
|
244
177
|
self._radioGB.setChecked(True)
|
178
|
+
elif mode is axis_mode._InputType.COMPOSITE:
|
179
|
+
# those mode are neither sinogram neither radio. Now one of the two will be checked but without any much meaning
|
180
|
+
self._radioGB.setEnabled(False)
|
181
|
+
self._sinogramGB.setEnabled(False)
|
245
182
|
else:
|
246
183
|
raise ValueError(f"Nothing implemented for {mode.value}")
|
247
184
|
|
248
|
-
def getCompositeOptions(self) -> dict:
|
249
|
-
return {
|
250
|
-
"theta": self.getTheta(),
|
251
|
-
"oversampling": self.getOversampling(),
|
252
|
-
"n_subsampling_y": self.getSubsamplingY(),
|
253
|
-
"take_log": self.getTakeLog(),
|
254
|
-
"near_pos": self.getNearpos(),
|
255
|
-
"near_width": self.getNearwidth(),
|
256
|
-
}
|
257
|
-
|
258
|
-
def setCompositeOptions(self, opts: dict) -> None:
|
259
|
-
if not isinstance(opts, dict):
|
260
|
-
raise TypeError("opts should be an instance of dict")
|
261
|
-
for key in opts.keys():
|
262
|
-
if key not in (
|
263
|
-
"theta",
|
264
|
-
"oversampling",
|
265
|
-
"n_subsampling_y",
|
266
|
-
"take_log",
|
267
|
-
"near_pos",
|
268
|
-
"near_width",
|
269
|
-
):
|
270
|
-
raise KeyError(f"{key} is not recogized")
|
271
|
-
theta = opts.get("theta", None)
|
272
|
-
if theta is not None:
|
273
|
-
self.setTheta(theta=theta)
|
274
|
-
oversampling = opts.get("oversampling", None)
|
275
|
-
if oversampling is not None:
|
276
|
-
self.setOversampling(oversampling)
|
277
|
-
n_subsampling_y = opts.get("n_subsampling_y", None)
|
278
|
-
if n_subsampling_y is not None:
|
279
|
-
self.setSubsamplingY(n_subsampling_y)
|
280
|
-
|
281
|
-
near_width = opts.get("near_width", None)
|
282
|
-
if near_width is not None:
|
283
|
-
self.setNearwidth(near_width)
|
284
|
-
|
285
|
-
take_log = opts.get("take_log", None)
|
286
|
-
if take_log is not None:
|
287
|
-
self.setTakeLog(take_log)
|
288
|
-
|
289
|
-
def getTheta(self) -> int:
|
290
|
-
return self._thetaSB.value()
|
291
|
-
|
292
|
-
def setTheta(self, theta: int) -> None:
|
293
|
-
self._thetaSB.setValue(theta)
|
294
|
-
|
295
|
-
def getOversampling(self) -> int:
|
296
|
-
return self._oversamplingSB.value()
|
297
|
-
|
298
|
-
def setOversampling(self, oversampling: int) -> None:
|
299
|
-
self._oversamplingSB.setValue(oversampling)
|
300
|
-
|
301
|
-
def getNearpos(self) -> int:
|
302
|
-
cal_widget = self.parentWidget().widget(0)
|
303
|
-
assert isinstance(cal_widget, CalculationWidget)
|
304
|
-
return cal_widget.getEstimatedCor()
|
305
|
-
|
306
|
-
def setNearpos(self, value) -> int:
|
307
|
-
cal_widget = self.parentWidget().widget(0)
|
308
|
-
assert isinstance(cal_widget, CalculationWidget)
|
309
|
-
cal_widget.setNearPosition(value)
|
310
|
-
|
311
|
-
def getNearwidth(self) -> int:
|
312
|
-
return self._nearwidthSB.value()
|
313
|
-
|
314
|
-
def setNearwidth(self, value) -> int:
|
315
|
-
return self._nearwidthSB.setValue(value)
|
316
|
-
|
317
|
-
def getSubsamplingY(self) -> int:
|
318
|
-
return self._subsamplingYSB.value()
|
319
|
-
|
320
|
-
def setSubsamplingY(self, subsampling: int) -> None:
|
321
|
-
self._subsamplingYSB.setValue(subsampling)
|
322
|
-
|
323
|
-
def getTakeLog(self) -> bool:
|
324
|
-
return self._takeLogCB.isChecked()
|
325
|
-
|
326
|
-
def setTakeLog(self, log: bool) -> None:
|
327
|
-
self._takeLogCB.setChecked(log)
|
328
|
-
|
329
185
|
|
330
186
|
class _AngleSelectionWidget(qt.QWidget):
|
331
187
|
"""Group box to select the angle to used for cor calculation
|
@@ -16,6 +16,10 @@ from silx.gui.plot import items
|
|
16
16
|
from silx.utils.enum import Enum as _Enum
|
17
17
|
from tomoscan.esrf.scan.utils import get_data
|
18
18
|
|
19
|
+
from tomwer.core.process.reconstruction.utils.cor import (
|
20
|
+
absolute_pos_to_relative,
|
21
|
+
relative_pos_to_absolute,
|
22
|
+
)
|
19
23
|
from tomwer.core.process.reconstruction.saaxis.saaxis import ReconstructionMode
|
20
24
|
from tomwer.core.scan.scanbase import TomwerScanBase
|
21
25
|
from tomwer.gui import icons
|
@@ -23,6 +27,7 @@ from tomwer.gui.reconstruction.saaxis.sliceselector import SliceSelector
|
|
23
27
|
from tomwer.gui.utils.lineselector import QSliceSelectorDialog
|
24
28
|
from tomwer.gui.utils.slider import LogSlider
|
25
29
|
from tomwer.gui.visualization.sinogramviewer import SinogramViewer as _SinogramViewer
|
30
|
+
from tomwer.gui.fonts import FONT_MEDIUM, FONT_IMPORTANT
|
26
31
|
|
27
32
|
_logger = logging.getLogger(__name__)
|
28
33
|
|
@@ -493,7 +498,9 @@ class _EstimatedCorWidget(qt.QGroupBox):
|
|
493
498
|
return 0.0
|
494
499
|
elif mode in ("abs", "absolute"):
|
495
500
|
if self._frameWidth is not None:
|
496
|
-
return (
|
501
|
+
return relative_pos_to_absolute(
|
502
|
+
relative_pos=0.0, det_width=self._frameWidth
|
503
|
+
)
|
497
504
|
else:
|
498
505
|
return self._MIDDLE_COR_TXT
|
499
506
|
else:
|
@@ -519,7 +526,9 @@ class _EstimatedCorWidget(qt.QGroupBox):
|
|
519
526
|
old = self.blockSignals(True)
|
520
527
|
old_mcor = self._manualCORAbs.blockSignals(True)
|
521
528
|
self._manualCORAbs.setValue(
|
522
|
-
|
529
|
+
relative_pos_to_absolute(
|
530
|
+
relative_pos=self._manualCORRel.value(), det_width=self._frameWidth
|
531
|
+
)
|
523
532
|
)
|
524
533
|
self._manualCORAbs.blockSignals(old_mcor)
|
525
534
|
self.blockSignals(old)
|
@@ -529,7 +538,10 @@ class _EstimatedCorWidget(qt.QGroupBox):
|
|
529
538
|
old = self.blockSignals(True)
|
530
539
|
old_mcor = self._manualCORRel.blockSignals(True)
|
531
540
|
self._manualCORRel.setValue(
|
532
|
-
|
541
|
+
absolute_pos_to_relative(
|
542
|
+
absolute_pos=self._manualCORAbs.value(),
|
543
|
+
det_width=self._frameWidth,
|
544
|
+
)
|
533
545
|
)
|
534
546
|
self._manualCORRel.blockSignals(old_mcor)
|
535
547
|
self.blockSignals(old)
|
@@ -582,15 +594,12 @@ class SAAxisOptions(qt.QWidget):
|
|
582
594
|
sigConfigurationChanged = qt.Signal()
|
583
595
|
"""signal emitted when the configuration change"""
|
584
596
|
|
585
|
-
_FONT_IMPORTANT = qt.QFont("Arial", 12)
|
586
|
-
_FONT_MEDIUM = qt.QFont("Arial", 10)
|
587
|
-
|
588
597
|
def __init__(self, parent=None):
|
589
598
|
qt.QWidget.__init__(self, parent)
|
590
599
|
self.setLayout(qt.QVBoxLayout())
|
591
600
|
# reconstruction mode
|
592
601
|
self._reconstructionMode = _ReconstructionModeGB(parent=self)
|
593
|
-
self._reconstructionMode.setFont(
|
602
|
+
self._reconstructionMode.setFont(FONT_MEDIUM)
|
594
603
|
self.layout().addWidget(self._reconstructionMode)
|
595
604
|
|
596
605
|
# estimated cor
|
@@ -598,12 +607,12 @@ class SAAxisOptions(qt.QWidget):
|
|
598
607
|
self,
|
599
608
|
title="Estimated cor position (x axis)",
|
600
609
|
)
|
601
|
-
self._estimatedCorWidget.setFont(
|
610
|
+
self._estimatedCorWidget.setFont(FONT_IMPORTANT)
|
602
611
|
self.layout().addWidget(self._estimatedCorWidget)
|
603
612
|
|
604
613
|
# detection accuracy
|
605
614
|
self._detectionAccuracy = _DetectionAccuracyGB(parent=self)
|
606
|
-
self._detectionAccuracy.setFont(
|
615
|
+
self._detectionAccuracy.setFont(FONT_MEDIUM)
|
607
616
|
self.layout().addWidget(self._detectionAccuracy)
|
608
617
|
|
609
618
|
# number of reconstructions eq volume size
|
@@ -613,7 +622,7 @@ class SAAxisOptions(qt.QWidget):
|
|
613
622
|
self._nReconsSB = qt.QSpinBox(self._nReconsWidget)
|
614
623
|
self._nReconsSB.setRange(0, 2000)
|
615
624
|
self._nReconsSB.setValue(30)
|
616
|
-
self._nReconsWidget.setFont(
|
625
|
+
self._nReconsWidget.setFont(FONT_IMPORTANT)
|
617
626
|
self._nReconsWidget.layout().addRow("Number of reconstruction", self._nReconsSB)
|
618
627
|
self.layout().addWidget(self._nReconsWidget)
|
619
628
|
|
@@ -14,6 +14,7 @@ from matplotlib import image as _matplotlib_image
|
|
14
14
|
from silx.gui import qt
|
15
15
|
from silx.gui.plot import PlotWidget
|
16
16
|
|
17
|
+
from tomwer.core.process.reconstruction.utils.cor import relative_pos_to_absolute
|
17
18
|
from tomwer.core.process.reconstruction.scores.params import ScoreMethod
|
18
19
|
from tomwer.gui import icons, settings
|
19
20
|
from tomwer.gui.reconstruction.saaxis.dimensionwidget import DimensionWidget
|
@@ -307,7 +308,9 @@ class CorSelection(VariableSelection):
|
|
307
308
|
if relative_value is None or self._img_width is None:
|
308
309
|
self._absoluteVarValueLE.clear()
|
309
310
|
else:
|
310
|
-
absolute_value =
|
311
|
+
absolute_value = relative_pos_to_absolute(
|
312
|
+
relative_pos=relative_value, det_width=self._img_width
|
313
|
+
)
|
311
314
|
self._absoluteVarValueLE.setText(f"{absolute_value:.3f}")
|
312
315
|
|
313
316
|
def getWindowTitle(self):
|
@@ -422,7 +425,7 @@ class _VariableValueLabels(qt.QWidget):
|
|
422
425
|
# paint oblique text
|
423
426
|
with PainterRotationCM(
|
424
427
|
painter=painter,
|
425
|
-
x=var_px_pos + self._slider_ticks_margin / 2.0,
|
428
|
+
x=var_px_pos + (self._slider_ticks_margin / 2.0),
|
426
429
|
y=0,
|
427
430
|
angle=self.rotation_angle_degree,
|
428
431
|
) as l_painter:
|
@@ -19,8 +19,11 @@ from tomwer.gui.reconstruction.nabu.nabuconfig.reconstruction import (
|
|
19
19
|
)
|
20
20
|
from tomwer.gui.reconstruction.nabu.nabuflow import NabuFlowControl
|
21
21
|
from tomwer.gui.reconstruction.nabu.volume import NabuVolumeTabWidget
|
22
|
+
from tomwer.gui.reconstruction.axis.EstimatedCORWidget import EstimatedCORWidget
|
22
23
|
from tomwer.tests.utils import skip_gui_test
|
23
24
|
from tomwer.core.process.output import ProcessDataOutputDirMode
|
25
|
+
from tomwer.tests.conftest import qtapp # noqa F401
|
26
|
+
from tomwer.synctools.axis import QAxisRP
|
24
27
|
|
25
28
|
|
26
29
|
class ProcessClass:
|
@@ -356,3 +359,8 @@ class TestNabuVolumeWidget(TestCaseQt):
|
|
356
359
|
}
|
357
360
|
)
|
358
361
|
self.assertEqual(self.nabuWidget.getConfiguration(), conf)
|
362
|
+
|
363
|
+
|
364
|
+
def test_EstimatedCorWidget(qtapp): # noqa F811
|
365
|
+
"""test of EstimatedCorWidget"""
|
366
|
+
EstimatedCORWidget(parent=None, axis_params=QAxisRP())
|
@@ -73,7 +73,7 @@ class Axis_N_Params(qt.QGroupBox):
|
|
73
73
|
|
74
74
|
class AutoRefineWidget(qt.QWidget):
|
75
75
|
"""
|
76
|
-
widget grouping information not specific to objects (output file,
|
76
|
+
widget grouping information not specific to objects (output file, stitching strategy...)
|
77
77
|
"""
|
78
78
|
|
79
79
|
def __init__(self, parent=None):
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import pytest
|
2
|
+
import numpy
|
2
3
|
|
3
4
|
from tomwer.gui.reconstruction.axis.AxisSettingsWidget import AxisSettingsTabWidget
|
4
5
|
from tomwer.synctools.axis import QAxisRP
|
@@ -10,18 +11,33 @@ from tomwer.tests.conftest import qtapp # noqa F401
|
|
10
11
|
def test_get_nabu_cor_opts(qtapp): # noqa F811
|
11
12
|
axis_params = QAxisRP()
|
12
13
|
widget = AxisSettingsTabWidget(recons_params=axis_params)
|
13
|
-
assert axis_params.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
widget.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
)
|
14
|
+
assert axis_params.get_nabu_cor_options_as_dict() == {
|
15
|
+
"side": "right",
|
16
|
+
"radio_angles": (0.0, numpy.pi),
|
17
|
+
"slice_idx": "middle",
|
18
|
+
}
|
19
|
+
widget._optionsWidget._corOpts.setText("low_pass=2.0")
|
20
|
+
widget._optionsWidget._corOpts.editingFinished.emit()
|
21
|
+
assert axis_params.get_nabu_cor_options_as_dict() == {
|
22
|
+
"side": "right",
|
23
|
+
"radio_angles": (0.0, numpy.pi),
|
24
|
+
"slice_idx": "middle",
|
25
|
+
"low_pass": 2.0,
|
26
|
+
}
|
27
|
+
widget._optionsWidget._corOpts.setText("low_pass=2 ; high_pass=10")
|
28
|
+
widget._optionsWidget._corOpts.editingFinished.emit()
|
29
|
+
assert axis_params.get_nabu_cor_options_as_dict() == {
|
30
|
+
"side": "right",
|
31
|
+
"radio_angles": (0.0, numpy.pi),
|
32
|
+
"slice_idx": "middle",
|
33
|
+
"low_pass": 2.0,
|
34
|
+
"high_pass": 10.0,
|
35
|
+
}
|
36
|
+
widget._calculationWidget.setEstimatedCorValue("left")
|
37
|
+
assert axis_params.get_nabu_cor_options_as_dict() == {
|
38
|
+
"side": "left",
|
39
|
+
"radio_angles": (0.0, numpy.pi),
|
40
|
+
"slice_idx": "middle",
|
41
|
+
"low_pass": 2.0,
|
42
|
+
"high_pass": 10.0,
|
43
|
+
}
|
@@ -23,7 +23,7 @@ _logger = logging.getLogger(__name__)
|
|
23
23
|
|
24
24
|
|
25
25
|
class AxisProcessStack(FIFO, qt.QObject):
|
26
|
-
"""Implementation of the `.
|
26
|
+
"""Implementation of the `.AxisTask` but having a stack for treating
|
27
27
|
scans and making computation in threads"""
|
28
28
|
|
29
29
|
def __init__(self, axis_params, process_id=None):
|
@@ -55,7 +55,7 @@ class AxisProcessStack(FIFO, qt.QObject):
|
|
55
55
|
if callback is not None:
|
56
56
|
callback()
|
57
57
|
self.scan_ready(scan=data)
|
58
|
-
elif
|
58
|
+
elif mode is (AxisMode.manual,):
|
59
59
|
# if cor is not set then set it to 0 (can be the case if no)
|
60
60
|
# interaction has been dne
|
61
61
|
cor = self._axis_params.relative_cor_value
|
@@ -84,14 +84,6 @@ class AxisProcessStack(FIFO, qt.QObject):
|
|
84
84
|
# we will keep the actual one (should have been defined previously)
|
85
85
|
self._end_computation(data=data, future_tomo_obj=None, callback=callback)
|
86
86
|
|
87
|
-
elif (
|
88
|
-
not self._axis_params.use_sinogram
|
89
|
-
and mode not in AxisTask._CALCULATIONS_METHODS
|
90
|
-
):
|
91
|
-
_logger.warning(f"no method defined to compute {mode}")
|
92
|
-
if callback is not None:
|
93
|
-
callback()
|
94
|
-
self._process_next()
|
95
87
|
else:
|
96
88
|
_logger.processStarted(
|
97
89
|
f"Start cor calculation on {data} ({self._axis_params.get_simple_str()})"
|
@@ -113,7 +105,7 @@ class AxisProcessStack(FIFO, qt.QObject):
|
|
113
105
|
"""
|
114
106
|
assert isinstance(data, TomwerScanBase)
|
115
107
|
assert self._axis_params is not None
|
116
|
-
# copy result computed on scan on the
|
108
|
+
# copy result computed on scan on the AxisTask reconsparams
|
117
109
|
self._axis_params.set_relative_value(
|
118
110
|
data.axis_params.relative_cor_value
|
119
111
|
) # noqa
|
@@ -167,23 +159,13 @@ class _ProcessingThread(ProcessingThread, SuperviseProcess):
|
|
167
159
|
},
|
168
160
|
process_id=self.process_id,
|
169
161
|
)
|
170
|
-
axis = self.apply_patch(axis=axis)
|
171
162
|
try:
|
172
163
|
axis.run()
|
173
164
|
except NoAxisUrl as e:
|
174
165
|
self.center_of_rotation = None
|
175
|
-
_logger.error(
|
166
|
+
_logger.error(f"CoR calculation failed. Issue with input ({e})")
|
176
167
|
except Exception as e:
|
177
|
-
_logger.error(
|
168
|
+
_logger.error(f"CoR calculation failed ({e})", stack_info=True)
|
178
169
|
self.center_of_rotation = None
|
179
170
|
else:
|
180
171
|
self.center_of_rotation = self._scan.axis_params.relative_cor_value
|
181
|
-
|
182
|
-
def patch_calc_method(self, mode, function):
|
183
|
-
self.__patch[mode] = function
|
184
|
-
|
185
|
-
def apply_patch(self, axis):
|
186
|
-
for mode, patch_fct in self.__patch.items():
|
187
|
-
if mode in AxisMode:
|
188
|
-
axis._CALCULATIONS_METHODS[mode] = patch_fct
|
189
|
-
return axis
|
@@ -23,7 +23,7 @@ _logger = logging.getLogger(__name__)
|
|
23
23
|
|
24
24
|
|
25
25
|
class DarkRefCopyProcessStack(FIFO, qt.QObject):
|
26
|
-
"""Implementation of the `.
|
26
|
+
"""Implementation of the `.AxisTask` but having a stack for treating
|
27
27
|
scans and making computation in threads"""
|
28
28
|
|
29
29
|
sigRefSetted = qt.Signal(str)
|
@@ -21,7 +21,7 @@ _logger = logging.getLogger(__name__)
|
|
21
21
|
|
22
22
|
|
23
23
|
class NabuSliceProcessStack(FIFO, qt.QObject):
|
24
|
-
"""Implementation of the `.
|
24
|
+
"""Implementation of the `.AxisTask` but having a stack for treating
|
25
25
|
scans and making computation in threads"""
|
26
26
|
|
27
27
|
def __init__(self, parent=None, process_id=None):
|
@@ -78,7 +78,7 @@ class NabuSliceProcessStack(FIFO, qt.QObject):
|
|
78
78
|
|
79
79
|
|
80
80
|
class NabuVolumeProcessStack(NabuSliceProcessStack):
|
81
|
-
"""Implementation of the `.
|
81
|
+
"""Implementation of the `.AxisTask` but having a stack for treating
|
82
82
|
scans and making computation in threads"""
|
83
83
|
|
84
84
|
def _create_processing_thread(self, process_id=None) -> qt.QThread:
|
@@ -20,7 +20,7 @@ _logger = logging.getLogger(__name__)
|
|
20
20
|
|
21
21
|
|
22
22
|
class INormalizationProcessStack(FIFO, qt.QObject):
|
23
|
-
"""Implementation of the `.
|
23
|
+
"""Implementation of the `.AxisTask` but having a stack for treating
|
24
24
|
scans and making computation in threads"""
|
25
25
|
|
26
26
|
def __init__(self, process_id=None):
|
@@ -22,7 +22,7 @@ _logger = logging.getLogger(__name__)
|
|
22
22
|
|
23
23
|
|
24
24
|
class SAAxisProcessStack(FIFO, qt.QObject):
|
25
|
-
"""Implementation of the `.
|
25
|
+
"""Implementation of the `.AxisTask` but having a stack for treating
|
26
26
|
scans and making computation in threads"""
|
27
27
|
|
28
28
|
def __init__(self, saaxis_params, process_id=None):
|
@@ -24,7 +24,7 @@ _logger = logging.getLogger(__name__)
|
|
24
24
|
|
25
25
|
|
26
26
|
class SADeltaBetaProcessStack(FIFO, qt.QObject):
|
27
|
-
"""Implementation of the `.
|
27
|
+
"""Implementation of the `.AxisTask` but having a stack for treating
|
28
28
|
scans and making computation in threads"""
|
29
29
|
|
30
30
|
def __init__(self, sa_delta_beta_params, process_id=None):
|
@@ -179,22 +179,6 @@ class TestAxisStack(TestCaseQt):
|
|
179
179
|
self.assertEqual(self._scan2.axis_params, None)
|
180
180
|
self.assertEqual(self._scan3.axis_params, None)
|
181
181
|
|
182
|
-
def testUnlockStack(self):
|
183
|
-
"""Check that all axis position will be computed properly if we set a
|
184
|
-
stack of scan"""
|
185
|
-
self._mainWindow.recons_params.set_relative_value(1.0)
|
186
|
-
for scan in (self._scan1, self._scan2, self._scan3):
|
187
|
-
self._mainWindow.process(scan)
|
188
|
-
|
189
|
-
for i in range(5):
|
190
|
-
self.qapp.processEvents()
|
191
|
-
time.sleep(0.2)
|
192
|
-
self.qapp.processEvents()
|
193
|
-
|
194
|
-
self.assertNotEqual(self._scan1.axis_params, None)
|
195
|
-
self.assertNotEqual(self._scan2.axis_params, None)
|
196
|
-
self.assertNotEqual(self._scan3.axis_params, None)
|
197
|
-
|
198
182
|
def testLockStack(self):
|
199
183
|
"""Check that axis position will be simply copy if we are in a lock
|
200
184
|
stack"""
|
@@ -37,7 +37,7 @@ pytest.mark.skipif(condition=not has_nabu, reason="nabu not installed")
|
|
37
37
|
|
38
38
|
classes_to_test = {
|
39
39
|
"darkref": "tomwer.core.process.reconstruction.darkref.darkrefs.DarkRefs",
|
40
|
-
"axis": "tomwer.core.process.reconstruction.axis.axis.
|
40
|
+
"axis": "tomwer.core.process.reconstruction.axis.axis.AxisTask",
|
41
41
|
"nabu slices": "tomwer.core.process.reconstruction.nabu.nabuslices.NabuSlices",
|
42
42
|
}
|
43
43
|
|
@@ -55,7 +55,7 @@ def test_simple_workflow_nabu():
|
|
55
55
|
{
|
56
56
|
"id": "axis",
|
57
57
|
"task_type": "class",
|
58
|
-
"task_identifier": "tomwer.core.process.reconstruction.axis.axis.
|
58
|
+
"task_identifier": "tomwer.core.process.reconstruction.axis.axis.AxisTask",
|
59
59
|
"default_inputs": [
|
60
60
|
{
|
61
61
|
"name": "axis_params",
|
tomwer/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: tomwer
|
3
|
-
Version: 1.4.
|
3
|
+
Version: 1.4.0rc1
|
4
4
|
Summary: "tomography workflow tools"
|
5
5
|
Home-page: https://gitlab.esrf.fr/tomotools/tomwer
|
6
6
|
Author: Henri Payno, Pierre Paleo, Pierre-Olivier Autran, Jérôme Lesaint, Alessandro Mirone
|
@@ -28,7 +28,7 @@ Requires-Dist: silx[full]>=2.0
|
|
28
28
|
Requires-Dist: tomoscan>=2.1.0a18
|
29
29
|
Requires-Dist: nxtomo>=1.3.0dev4
|
30
30
|
Requires-Dist: nxtomomill>=1.1.0a0
|
31
|
-
Requires-Dist: processview>=1.3
|
31
|
+
Requires-Dist: processview>=1.4.3
|
32
32
|
Requires-Dist: ewoks>=0.1.1
|
33
33
|
Requires-Dist: sluurp>=0.4.0
|
34
34
|
Requires-Dist: packaging
|