shinestacker 1.3.0__py3-none-any.whl → 1.4.0__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.
Potentially problematic release.
This version of shinestacker might be problematic. Click here for more details.
- shinestacker/_version.py +1 -1
- shinestacker/algorithms/align.py +229 -41
- shinestacker/algorithms/align_auto.py +15 -3
- shinestacker/algorithms/align_parallel.py +81 -25
- shinestacker/algorithms/balance.py +23 -13
- shinestacker/algorithms/base_stack_algo.py +14 -20
- shinestacker/algorithms/depth_map.py +9 -14
- shinestacker/algorithms/noise_detection.py +3 -1
- shinestacker/algorithms/pyramid.py +8 -22
- shinestacker/algorithms/pyramid_auto.py +5 -14
- shinestacker/algorithms/pyramid_tiles.py +18 -20
- shinestacker/algorithms/stack_framework.py +1 -1
- shinestacker/algorithms/utils.py +37 -10
- shinestacker/algorithms/vignetting.py +2 -0
- shinestacker/app/gui_utils.py +10 -0
- shinestacker/app/main.py +3 -1
- shinestacker/app/project.py +3 -1
- shinestacker/app/retouch.py +3 -1
- shinestacker/config/gui_constants.py +2 -2
- shinestacker/core/core_utils.py +10 -1
- shinestacker/gui/action_config.py +172 -7
- shinestacker/gui/action_config_dialog.py +443 -452
- shinestacker/gui/colors.py +1 -0
- shinestacker/gui/folder_file_selection.py +5 -0
- shinestacker/gui/gui_run.py +2 -2
- shinestacker/gui/main_window.py +18 -9
- shinestacker/gui/menu_manager.py +26 -2
- shinestacker/gui/new_project.py +5 -5
- shinestacker/gui/project_controller.py +4 -0
- shinestacker/gui/project_editor.py +6 -4
- shinestacker/gui/recent_file_manager.py +93 -0
- shinestacker/gui/sys_mon.py +24 -23
- shinestacker/retouch/base_filter.py +5 -5
- shinestacker/retouch/brush_preview.py +3 -0
- shinestacker/retouch/brush_tool.py +11 -11
- shinestacker/retouch/display_manager.py +21 -37
- shinestacker/retouch/image_editor_ui.py +129 -71
- shinestacker/retouch/image_view_status.py +61 -0
- shinestacker/retouch/image_viewer.py +89 -431
- shinestacker/retouch/io_gui_handler.py +12 -2
- shinestacker/retouch/overlaid_view.py +212 -0
- shinestacker/retouch/shortcuts_help.py +13 -3
- shinestacker/retouch/sidebyside_view.py +479 -0
- shinestacker/retouch/view_strategy.py +466 -0
- {shinestacker-1.3.0.dist-info → shinestacker-1.4.0.dist-info}/METADATA +1 -1
- {shinestacker-1.3.0.dist-info → shinestacker-1.4.0.dist-info}/RECORD +50 -45
- {shinestacker-1.3.0.dist-info → shinestacker-1.4.0.dist-info}/WHEEL +0 -0
- {shinestacker-1.3.0.dist-info → shinestacker-1.4.0.dist-info}/entry_points.txt +0 -0
- {shinestacker-1.3.0.dist-info → shinestacker-1.4.0.dist-info}/licenses/LICENSE +0 -0
- {shinestacker-1.3.0.dist-info → shinestacker-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, E0611, R0913, R0917, R0915, R0912
|
|
2
|
-
# pylint: disable=E0606, W0718, R1702, W0102, W0221, R0914
|
|
2
|
+
# pylint: disable=E0606, W0718, R1702, W0102, W0221, R0914, C0302
|
|
3
3
|
import os
|
|
4
4
|
import traceback
|
|
5
|
-
from PySide6.QtWidgets import (QWidget, QPushButton, QHBoxLayout, QLabel, QScrollArea, QSizePolicy,
|
|
6
|
-
QMessageBox, QStackedWidget, QFormLayout, QDialog)
|
|
7
5
|
from PySide6.QtCore import Qt, QTimer
|
|
6
|
+
from PySide6.QtWidgets import (QWidget, QPushButton, QHBoxLayout, QLabel, QScrollArea, QMessageBox,
|
|
7
|
+
QStackedWidget, QFormLayout, QDialog)
|
|
8
8
|
from .. config.constants import constants
|
|
9
9
|
from .. algorithms.align import validate_align_config
|
|
10
|
-
from .project_model import ActionConfig
|
|
11
10
|
from .base_form_dialog import create_form_layout
|
|
12
11
|
from . action_config import (
|
|
13
|
-
|
|
12
|
+
DefaultActionConfigurator,
|
|
14
13
|
FIELD_TEXT, FIELD_ABS_PATH, FIELD_REL_PATH, FIELD_FLOAT,
|
|
15
14
|
FIELD_INT, FIELD_INT_TUPLE, FIELD_BOOL, FIELD_COMBO, FIELD_REF_IDX
|
|
16
15
|
)
|
|
@@ -18,7 +17,7 @@ from .folder_file_selection import FolderFileSelectionWidget
|
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
class ActionConfigDialog(QDialog):
|
|
21
|
-
def __init__(self, action
|
|
20
|
+
def __init__(self, action, current_wd, parent=None):
|
|
22
21
|
super().__init__(parent)
|
|
23
22
|
self.setWindowTitle(f"Configure {action.type_name}")
|
|
24
23
|
self.form_layout = create_form_layout(self)
|
|
@@ -85,7 +84,7 @@ class ActionConfigDialog(QDialog):
|
|
|
85
84
|
frame_geometry.moveCenter(center_point)
|
|
86
85
|
self.move(frame_geometry.topLeft())
|
|
87
86
|
|
|
88
|
-
def get_configurator(self, action_type
|
|
87
|
+
def get_configurator(self, action_type):
|
|
89
88
|
configurators = {
|
|
90
89
|
constants.ACTION_JOB: JobConfigurator,
|
|
91
90
|
constants.ACTION_COMBO: CombinedActionsConfigurator,
|
|
@@ -115,58 +114,9 @@ class ActionConfigDialog(QDialog):
|
|
|
115
114
|
return self.parent().expert_options
|
|
116
115
|
|
|
117
116
|
|
|
118
|
-
class NoNameActionConfigurator(ActionConfigurator):
|
|
119
|
-
def __init__(self, expert, current_wd):
|
|
120
|
-
super().__init__(expert, current_wd)
|
|
121
|
-
self.builder = None
|
|
122
|
-
|
|
123
|
-
def get_builder(self):
|
|
124
|
-
return self.builder
|
|
125
|
-
|
|
126
|
-
def update_params(self, params):
|
|
127
|
-
return self.builder.update_params(params)
|
|
128
|
-
|
|
129
|
-
def add_bold_label(self, label):
|
|
130
|
-
label = QLabel(label)
|
|
131
|
-
label.setStyleSheet("font-weight: bold")
|
|
132
|
-
self.add_row(label)
|
|
133
|
-
|
|
134
|
-
def add_row(self, row):
|
|
135
|
-
self.builder.main_layout.addRow(row)
|
|
136
|
-
|
|
137
|
-
def add_field(self, tag, field_type, label,
|
|
138
|
-
required=False, add_to_layout=None, **kwargs):
|
|
139
|
-
return self.builder.add_field(tag, field_type, label, required, add_to_layout, **kwargs)
|
|
140
|
-
|
|
141
|
-
def labelled_widget(self, label, widget):
|
|
142
|
-
row = QWidget()
|
|
143
|
-
layout = QHBoxLayout()
|
|
144
|
-
layout.setContentsMargins(2, 2, 2, 2)
|
|
145
|
-
layout.setSpacing(8)
|
|
146
|
-
label_widget = QLabel(label)
|
|
147
|
-
label_widget.setFixedWidth(120)
|
|
148
|
-
label_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
|
|
149
|
-
layout.addWidget(label_widget)
|
|
150
|
-
layout.addWidget(widget)
|
|
151
|
-
layout.setStretch(0, 1)
|
|
152
|
-
layout.setStretch(1, 3)
|
|
153
|
-
row.setLayout(layout)
|
|
154
|
-
return row
|
|
155
|
-
|
|
156
|
-
def add_labelled_row(self, label, widget):
|
|
157
|
-
self.add_row(self.labelled_widget(label, widget))
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
class DefaultActionConfigurator(NoNameActionConfigurator):
|
|
161
|
-
def create_form(self, layout, action, tag='Action'):
|
|
162
|
-
self.builder = FieldBuilder(layout, action, self.current_wd)
|
|
163
|
-
self.add_field(
|
|
164
|
-
'name', FIELD_TEXT, f'{tag} name', required=True)
|
|
165
|
-
|
|
166
|
-
|
|
167
117
|
class JobConfigurator(DefaultActionConfigurator):
|
|
168
118
|
def __init__(self, expert, current_wd):
|
|
169
|
-
super().__init__(expert, current_wd)
|
|
119
|
+
super().__init__(expert, current_wd, expert_toggle=False)
|
|
170
120
|
self.working_path_label = None
|
|
171
121
|
self.input_path_label = None
|
|
172
122
|
self.frames_label = None
|
|
@@ -183,20 +133,11 @@ class JobConfigurator(DefaultActionConfigurator):
|
|
|
183
133
|
input_filepaths = input_filepaths.split(constants.PATH_SEPARATOR)
|
|
184
134
|
self.working_path_label = QLabel(working_path or "Not set")
|
|
185
135
|
self.input_path_label = QLabel(input_path or "Not set")
|
|
186
|
-
|
|
136
|
+
self.input_widget.path_edit.setText('')
|
|
187
137
|
if input_filepaths:
|
|
188
138
|
self.input_widget.files_mode_radio.setChecked(True)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
parent_dir = os.path.dirname(input_filepaths[0])
|
|
192
|
-
self.input_widget.path_edit.setText(parent_dir)
|
|
193
|
-
elif input_path and working_path:
|
|
194
|
-
self.input_widget.folder_mode_radio.setChecked(True)
|
|
195
|
-
input_fullpath = os.path.join(working_path, input_path)
|
|
196
|
-
self.input_widget.path_edit.setText(input_fullpath)
|
|
197
|
-
elif input_path:
|
|
198
|
-
self.input_widget.folder_mode_radio.setChecked(True)
|
|
199
|
-
self.input_widget.path_edit.setText(input_path)
|
|
139
|
+
else:
|
|
140
|
+
self.input_widget.folder_mode_radio.setChecked(False)
|
|
200
141
|
self.input_widget.text_changed_connect(self.update_paths_and_frames)
|
|
201
142
|
self.input_widget.folder_mode_radio.toggled.connect(self.update_paths_and_frames)
|
|
202
143
|
self.input_widget.files_mode_radio.toggled.connect(self.update_paths_and_frames)
|
|
@@ -206,29 +147,27 @@ class JobConfigurator(DefaultActionConfigurator):
|
|
|
206
147
|
self.add_bold_label("Derived Paths:")
|
|
207
148
|
self.add_labelled_row("Working path: ", self.working_path_label)
|
|
208
149
|
self.add_labelled_row("Input path:", self.input_path_label)
|
|
209
|
-
|
|
210
|
-
self.update_paths_and_frames()
|
|
211
|
-
else:
|
|
212
|
-
self.update_frames_count()
|
|
150
|
+
self.set_paths_and_frames(working_path, input_path, input_filepaths)
|
|
213
151
|
|
|
214
152
|
def update_frames_count(self):
|
|
215
153
|
if self.input_widget.get_selection_mode() == 'files':
|
|
216
|
-
count =
|
|
154
|
+
count = self.input_widget.num_selected_files()
|
|
217
155
|
else:
|
|
218
156
|
count = self.count_image_files(self.input_widget.get_path())
|
|
219
157
|
self.frames_label.setText(str(count))
|
|
220
158
|
|
|
221
|
-
def
|
|
159
|
+
def set_paths_and_frames(self, working_path, input_path, input_filepaths):
|
|
160
|
+
self.input_path_label.setText(input_path or "Not set")
|
|
161
|
+
self.working_path_label.setText(working_path or "Not set")
|
|
162
|
+
self.frames_label.setText(str(len(input_filepaths)))
|
|
163
|
+
|
|
164
|
+
def update_paths_and_frames(self, ):
|
|
165
|
+
input_fullpath = self.input_widget.get_path()
|
|
166
|
+
input_path = os.path.basename(os.path.normpath(input_fullpath)) if input_fullpath else ""
|
|
167
|
+
working_path = os.path.dirname(input_fullpath) if input_fullpath else ""
|
|
168
|
+
self.input_path_label.setText(input_path or "Not set")
|
|
169
|
+
self.working_path_label.setText(working_path or "Not set")
|
|
222
170
|
self.update_frames_count()
|
|
223
|
-
selection_mode = self.input_widget.get_selection_mode()
|
|
224
|
-
selected_files = self.input_widget.get_selected_files()
|
|
225
|
-
input_path = self.input_widget.get_path()
|
|
226
|
-
if selection_mode == 'files' and selected_files:
|
|
227
|
-
input_path = os.path.dirname(selected_files[0])
|
|
228
|
-
input_path_value = os.path.basename(os.path.normpath(input_path)) if input_path else ""
|
|
229
|
-
working_path_value = os.path.dirname(input_path) if input_path else ""
|
|
230
|
-
self.input_path_label.setText(input_path_value or "Not set")
|
|
231
|
-
self.working_path_label.setText(working_path_value or "Not set")
|
|
232
171
|
|
|
233
172
|
def count_image_files(self, path):
|
|
234
173
|
if not path or not os.path.isdir(path):
|
|
@@ -244,18 +183,16 @@ class JobConfigurator(DefaultActionConfigurator):
|
|
|
244
183
|
return False
|
|
245
184
|
selection_mode = self.input_widget.get_selection_mode()
|
|
246
185
|
selected_files = self.input_widget.get_selected_files()
|
|
247
|
-
input_path = self.input_widget.get_path()
|
|
248
186
|
if selection_mode == 'files' and selected_files:
|
|
187
|
+
input_full_path = os.path.dirname(selected_files[0])
|
|
249
188
|
params['input_filepaths'] = self.input_widget.get_selected_filenames()
|
|
250
|
-
params['input_path'] = os.path.dirname(selected_files[0])
|
|
251
189
|
else:
|
|
190
|
+
input_full_path = self.input_widget.get_path()
|
|
252
191
|
params['input_filepaths'] = []
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
else:
|
|
258
|
-
params['working_path'] = ''
|
|
192
|
+
input_path = os.path.basename(os.path.normpath(input_full_path)) if input_full_path else ""
|
|
193
|
+
working_path = os.path.dirname(input_full_path) if input_full_path else ""
|
|
194
|
+
params['input_path'] = input_path
|
|
195
|
+
params['working_path'] = working_path
|
|
259
196
|
return True
|
|
260
197
|
|
|
261
198
|
|
|
@@ -279,10 +216,10 @@ class NoiseDetectionConfigurator(DefaultActionConfigurator):
|
|
|
279
216
|
required=False, size=3,
|
|
280
217
|
default=constants.DEFAULT_CHANNEL_THRESHOLDS,
|
|
281
218
|
labels=constants.RGB_LABELS, min_val=[1] * 3, max_val=[1000] * 3)
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
219
|
+
self.add_field(
|
|
220
|
+
'blur_size', FIELD_INT, 'Blur size (px)', required=False,
|
|
221
|
+
expert=True,
|
|
222
|
+
default=constants.DEFAULT_BLUR_SIZE, min_val=1, max_val=50)
|
|
286
223
|
self.add_field(
|
|
287
224
|
'file_name', FIELD_TEXT, 'File name', required=False,
|
|
288
225
|
default=constants.DEFAULT_NOISE_MAP_FILENAME,
|
|
@@ -307,38 +244,45 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
|
|
|
307
244
|
FLOAT_OPTIONS = ['float 32 bits', 'float 64 bits']
|
|
308
245
|
MODE_OPTIONS = ['Auto', 'All in memory', 'Tiled I/O buffered']
|
|
309
246
|
|
|
247
|
+
def __init__(self, expert, current_wd):
|
|
248
|
+
super().__init__(expert, current_wd)
|
|
249
|
+
self.tab_widget = None
|
|
250
|
+
self.general_tab_layout = None
|
|
251
|
+
self.algorithm_tab_layout = None
|
|
252
|
+
|
|
310
253
|
def create_form(self, layout, action):
|
|
311
254
|
super().create_form(layout, action)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
255
|
+
self.tab_widget = self.create_tab_widget(layout)
|
|
256
|
+
self.general_tab_layout = self.add_tab(self.tab_widget, "General Parameters")
|
|
257
|
+
self.create_general_tab(self.general_tab_layout)
|
|
258
|
+
self.algorithm_tab_layout = self.add_tab(self.tab_widget, "Stacking Algorithm")
|
|
259
|
+
self.create_algorithm_tab(self.algorithm_tab_layout)
|
|
260
|
+
|
|
261
|
+
def create_general_tab(self, layout):
|
|
262
|
+
self.add_field_to_layout(
|
|
263
|
+
layout, 'working_path', FIELD_ABS_PATH, 'Working path', required=False,
|
|
264
|
+
expert=True)
|
|
265
|
+
self.add_field_to_layout(
|
|
266
|
+
layout, 'input_path', FIELD_REL_PATH, 'Input path', required=False,
|
|
267
|
+
expert=True,
|
|
268
|
+
placeholder='relative to working path')
|
|
269
|
+
self.add_field_to_layout(
|
|
270
|
+
layout, 'output_path', FIELD_REL_PATH, 'Output path', required=False,
|
|
271
|
+
expert=True,
|
|
272
|
+
placeholder='relative to working path')
|
|
273
|
+
self.add_field_to_layout(
|
|
274
|
+
layout, 'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
|
|
323
275
|
required=False, default=True)
|
|
324
276
|
|
|
325
|
-
def
|
|
326
|
-
self.
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
self.add_bold_label("Stacking algorithm:")
|
|
330
|
-
combo = self.add_field(
|
|
331
|
-
'stacker', FIELD_COMBO, 'Stacking algorithm', required=True,
|
|
277
|
+
def create_algorithm_tab(self, layout):
|
|
278
|
+
self.add_bold_label_to_layout(layout, "Stacking algorithm:")
|
|
279
|
+
combo = self.add_field_to_layout(
|
|
280
|
+
layout, 'stacker', FIELD_COMBO, 'Stacking algorithm', required=True,
|
|
332
281
|
options=constants.STACK_ALGO_OPTIONS,
|
|
333
282
|
default=constants.STACK_ALGO_DEFAULT)
|
|
334
283
|
q_pyramid, q_depthmap = QWidget(), QWidget()
|
|
335
284
|
for q in [q_pyramid, q_depthmap]:
|
|
336
|
-
|
|
337
|
-
layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
|
|
338
|
-
layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
|
|
339
|
-
layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)
|
|
340
|
-
layout.setLabelAlignment(Qt.AlignLeft)
|
|
341
|
-
q.setLayout(layout)
|
|
285
|
+
q.setLayout(self.create_tab_layout())
|
|
342
286
|
stacked = QStackedWidget()
|
|
343
287
|
stacked.addWidget(q_pyramid)
|
|
344
288
|
stacked.addWidget(q_depthmap)
|
|
@@ -349,158 +293,160 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
|
|
|
349
293
|
stacked.setCurrentWidget(q_pyramid)
|
|
350
294
|
elif text == constants.STACK_ALGO_DEPTH_MAP:
|
|
351
295
|
stacked.setCurrentWidget(q_depthmap)
|
|
296
|
+
|
|
352
297
|
change()
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
298
|
+
self.add_field_to_layout(
|
|
299
|
+
q_pyramid.layout(), 'pyramid_min_size', FIELD_INT, 'Minimum size (px)',
|
|
300
|
+
expert=True,
|
|
301
|
+
required=False, default=constants.DEFAULT_PY_MIN_SIZE, min_val=2, max_val=256)
|
|
302
|
+
self.add_field_to_layout(
|
|
303
|
+
q_pyramid.layout(), 'pyramid_kernel_size', FIELD_INT, 'Kernel size (px)',
|
|
304
|
+
expert=True,
|
|
305
|
+
required=False, default=constants.DEFAULT_PY_KERNEL_SIZE, min_val=3, max_val=21)
|
|
306
|
+
self.add_field_to_layout(
|
|
307
|
+
q_pyramid.layout(), 'pyramid_gen_kernel', FIELD_FLOAT, 'Gen. kernel',
|
|
308
|
+
expert=True,
|
|
309
|
+
required=False, default=constants.DEFAULT_PY_GEN_KERNEL,
|
|
310
|
+
min_val=0.0, max_val=2.0)
|
|
311
|
+
self.add_field_to_layout(
|
|
312
|
+
q_pyramid.layout(), 'pyramid_float_type', FIELD_COMBO, 'Precision', required=False,
|
|
313
|
+
expert=True,
|
|
314
|
+
options=self.FLOAT_OPTIONS, values=constants.VALID_FLOATS,
|
|
315
|
+
default=dict(zip(constants.VALID_FLOATS,
|
|
316
|
+
self.FLOAT_OPTIONS))[constants.DEFAULT_PY_FLOAT])
|
|
317
|
+
mode = self.add_field_to_layout(
|
|
318
|
+
q_pyramid.layout(), 'pyramid_mode', FIELD_COMBO, 'Mode',
|
|
319
|
+
expert=True,
|
|
320
|
+
required=False, options=self.MODE_OPTIONS, values=constants.PY_VALID_MODES,
|
|
321
|
+
default=dict(zip(constants.PY_VALID_MODES,
|
|
322
|
+
self.MODE_OPTIONS))[constants.DEFAULT_PY_MODE])
|
|
323
|
+
memory_limit = self.add_field_to_layout(
|
|
324
|
+
q_pyramid.layout(), 'pyramid_memory_limit', FIELD_FLOAT,
|
|
325
|
+
'Memory limit (approx., GBytes)',
|
|
326
|
+
expert=True,
|
|
327
|
+
required=False, default=constants.DEFAULT_PY_MEMORY_LIMIT_GB,
|
|
328
|
+
min_val=1.0, max_val=64.0)
|
|
329
|
+
max_threads = self.add_field_to_layout(
|
|
330
|
+
q_pyramid.layout(), 'pyramid_max_threads', FIELD_INT, 'Max num. of cores',
|
|
331
|
+
expert=True,
|
|
332
|
+
required=False, default=constants.DEFAULT_PY_MAX_THREADS,
|
|
333
|
+
min_val=1, max_val=64)
|
|
334
|
+
tile_size = self.add_field_to_layout(
|
|
335
|
+
q_pyramid.layout(), 'pyramid_tile_size', FIELD_INT, 'Tile size (px)',
|
|
336
|
+
expert=True,
|
|
337
|
+
required=False, default=constants.DEFAULT_PY_TILE_SIZE,
|
|
338
|
+
min_val=128, max_val=2048)
|
|
339
|
+
n_tiled_layers = self.add_field_to_layout(
|
|
340
|
+
q_pyramid.layout(), 'pyramid_n_tiled_layers', FIELD_INT, 'Num. tiled layers',
|
|
341
|
+
expert=True,
|
|
342
|
+
required=False, default=constants.DEFAULT_PY_N_TILED_LAYERS,
|
|
343
|
+
min_val=0, max_val=6)
|
|
344
|
+
|
|
345
|
+
def change_mode():
|
|
346
|
+
text = mode.currentText()
|
|
347
|
+
enabled = text == self.MODE_OPTIONS[2]
|
|
348
|
+
tile_size.setEnabled(enabled)
|
|
349
|
+
n_tiled_layers.setEnabled(enabled)
|
|
350
|
+
memory_limit.setEnabled(text == self.MODE_OPTIONS[0])
|
|
351
|
+
max_threads.setEnabled(text != self.MODE_OPTIONS[1])
|
|
352
|
+
|
|
353
|
+
mode.currentIndexChanged.connect(change_mode)
|
|
354
|
+
change_mode()
|
|
355
|
+
|
|
356
|
+
self.add_field_to_layout(
|
|
357
|
+
q_depthmap.layout(), 'depthmap_energy', FIELD_COMBO, 'Energy', required=False,
|
|
413
358
|
options=self.ENERGY_OPTIONS, values=constants.VALID_DM_ENERGY,
|
|
414
359
|
default=dict(zip(constants.VALID_DM_ENERGY,
|
|
415
360
|
self.ENERGY_OPTIONS))[constants.DEFAULT_DM_ENERGY])
|
|
416
|
-
self.
|
|
417
|
-
'map_type', FIELD_COMBO, 'Map type', required=False,
|
|
418
|
-
add_to_layout=q_depthmap.layout(),
|
|
361
|
+
self.add_field_to_layout(
|
|
362
|
+
q_depthmap.layout(), 'map_type', FIELD_COMBO, 'Map type', required=False,
|
|
419
363
|
options=self.MAP_TYPE_OPTIONS, values=constants.VALID_DM_MAP,
|
|
420
364
|
default=dict(zip(constants.VALID_DM_MAP,
|
|
421
365
|
self.MAP_TYPE_OPTIONS))[constants.DEFAULT_DM_MAP])
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
366
|
+
self.add_field_to_layout(
|
|
367
|
+
q_depthmap.layout(), 'depthmap_kernel_size', FIELD_INT, 'Kernel size (px)',
|
|
368
|
+
expert=True,
|
|
369
|
+
required=False, default=constants.DEFAULT_DM_KERNEL_SIZE, min_val=3, max_val=21)
|
|
370
|
+
self.add_field_to_layout(
|
|
371
|
+
q_depthmap.layout(), 'depthmap_blur_size', FIELD_INT, 'Blurl size (px)',
|
|
372
|
+
expert=True,
|
|
373
|
+
required=False, default=constants.DEFAULT_DM_BLUR_SIZE, min_val=1, max_val=21)
|
|
374
|
+
self.add_field_to_layout(
|
|
375
|
+
q_depthmap.layout(), 'depthmap_smooth_size', FIELD_INT, 'Smooth size (px)',
|
|
376
|
+
expert=True,
|
|
377
|
+
required=False, default=constants.DEFAULT_DM_SMOOTH_SIZE, min_val=0, max_val=256)
|
|
378
|
+
self.add_field_to_layout(
|
|
379
|
+
q_depthmap.layout(), 'depthmap_temperature', FIELD_FLOAT, 'Temperature',
|
|
380
|
+
expert=True,
|
|
381
|
+
required=False, default=constants.DEFAULT_DM_TEMPERATURE,
|
|
382
|
+
min_val=0, max_val=1, step=0.05)
|
|
383
|
+
self.add_field_to_layout(
|
|
384
|
+
q_depthmap.layout(), 'depthmap_levels', FIELD_INT, 'Levels', required=False,
|
|
385
|
+
expert=True,
|
|
386
|
+
default=constants.DEFAULT_DM_LEVELS, min_val=2, max_val=6)
|
|
387
|
+
self.add_field_to_layout(
|
|
388
|
+
q_depthmap.layout(), 'depthmap_float_type', FIELD_COMBO,
|
|
389
|
+
'Precision', required=False,
|
|
390
|
+
expert=True,
|
|
391
|
+
options=self.FLOAT_OPTIONS, values=constants.VALID_FLOATS,
|
|
392
|
+
default=dict(zip(constants.VALID_FLOATS,
|
|
393
|
+
self.FLOAT_OPTIONS))[constants.DEFAULT_DM_FLOAT])
|
|
394
|
+
layout.addRow(stacked)
|
|
451
395
|
combo.currentIndexChanged.connect(change)
|
|
452
396
|
|
|
453
397
|
|
|
454
398
|
class FocusStackConfigurator(FocusStackBaseConfigurator):
|
|
455
399
|
def create_form(self, layout, action):
|
|
456
400
|
super().create_form(layout, action)
|
|
457
|
-
|
|
458
|
-
self.
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
401
|
+
self.add_field_to_layout(
|
|
402
|
+
self.general_tab_layout, 'exif_path', FIELD_REL_PATH,
|
|
403
|
+
'Exif data path', required=False,
|
|
404
|
+
expert=True,
|
|
405
|
+
placeholder='relative to working path')
|
|
406
|
+
self.add_field_to_layout(
|
|
407
|
+
self.general_tab_layout, 'prefix', FIELD_TEXT,
|
|
408
|
+
'Output filename prefix', required=False,
|
|
409
|
+
expert=True,
|
|
410
|
+
default=constants.DEFAULT_STACK_PREFIX,
|
|
411
|
+
placeholder=constants.DEFAULT_STACK_PREFIX)
|
|
412
|
+
self.add_field_to_layout(
|
|
413
|
+
self.general_tab_layout, 'plot_stack', FIELD_BOOL, 'Plot stack', required=False,
|
|
467
414
|
default=constants.DEFAULT_PLOT_STACK)
|
|
468
|
-
super().common_fields(layout)
|
|
469
415
|
|
|
470
416
|
|
|
471
417
|
class FocusStackBunchConfigurator(FocusStackBaseConfigurator):
|
|
472
418
|
def create_form(self, layout, action):
|
|
473
419
|
super().create_form(layout, action)
|
|
474
|
-
self.
|
|
475
|
-
'frames', FIELD_INT, 'Frames', required=False,
|
|
420
|
+
self.add_field_to_layout(
|
|
421
|
+
self.general_tab_layout, 'frames', FIELD_INT, 'Frames', required=False,
|
|
476
422
|
default=constants.DEFAULT_FRAMES, min_val=1, max_val=100)
|
|
477
|
-
self.
|
|
478
|
-
'overlap', FIELD_INT, 'Overlapping frames', required=False,
|
|
423
|
+
self.add_field_to_layout(
|
|
424
|
+
self.general_tab_layout, 'overlap', FIELD_INT, 'Overlapping frames', required=False,
|
|
479
425
|
default=constants.DEFAULT_OVERLAP, min_val=0, max_val=100)
|
|
480
|
-
self.
|
|
481
|
-
'plot_stack', FIELD_BOOL, 'Plot stack', required=False,
|
|
426
|
+
self.add_field_to_layout(
|
|
427
|
+
self.general_tab_layout, 'plot_stack', FIELD_BOOL, 'Plot stack', required=False,
|
|
482
428
|
default=constants.DEFAULT_PLOT_STACK_BUNCH)
|
|
483
|
-
super().common_fields(layout)
|
|
484
429
|
|
|
485
430
|
|
|
486
431
|
class MultiLayerConfigurator(DefaultActionConfigurator):
|
|
487
432
|
def create_form(self, layout, action):
|
|
488
433
|
super().create_form(layout, action)
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
434
|
+
self.add_field(
|
|
435
|
+
'working_path', FIELD_ABS_PATH, 'Working path', required=False,
|
|
436
|
+
expert=True)
|
|
492
437
|
self.add_field(
|
|
493
438
|
'input_path', FIELD_REL_PATH,
|
|
494
439
|
f'Input path (separate by {constants.PATH_SEPARATOR})',
|
|
495
440
|
required=False, multiple_entries=True,
|
|
496
441
|
placeholder='relative to working path')
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
442
|
+
self.add_field(
|
|
443
|
+
'output_path', FIELD_REL_PATH, 'Output path', required=False,
|
|
444
|
+
expert=True,
|
|
445
|
+
placeholder='relative to working path')
|
|
446
|
+
self.add_field(
|
|
447
|
+
'exif_path', FIELD_REL_PATH, 'Exif data path', required=False,
|
|
448
|
+
expert=True,
|
|
449
|
+
placeholder='relative to working path')
|
|
504
450
|
self.add_field(
|
|
505
451
|
'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
|
|
506
452
|
required=False, default=True)
|
|
@@ -512,38 +458,45 @@ class MultiLayerConfigurator(DefaultActionConfigurator):
|
|
|
512
458
|
class CombinedActionsConfigurator(DefaultActionConfigurator):
|
|
513
459
|
def create_form(self, layout, action):
|
|
514
460
|
super().create_form(layout, action)
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
461
|
+
self.add_field(
|
|
462
|
+
'working_path', FIELD_ABS_PATH, 'Working path', required=False,
|
|
463
|
+
expert=True)
|
|
464
|
+
self.add_field(
|
|
465
|
+
'input_path', FIELD_REL_PATH, 'Input path', required=False,
|
|
466
|
+
expert=True,
|
|
467
|
+
must_exist=True, placeholder='relative to working path')
|
|
468
|
+
self.add_field(
|
|
469
|
+
'output_path', FIELD_REL_PATH, 'Output path', required=False,
|
|
470
|
+
expert=True,
|
|
471
|
+
placeholder='relative to working path')
|
|
524
472
|
self.add_field(
|
|
525
473
|
'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
|
|
526
474
|
required=False, default=True)
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
475
|
+
self.add_field(
|
|
476
|
+
'plot_path', FIELD_REL_PATH, 'Plots path', required=False,
|
|
477
|
+
expert=True,
|
|
478
|
+
default="plots", placeholder='relative to working path')
|
|
479
|
+
self.add_field(
|
|
480
|
+
'resample', FIELD_INT, 'Resample frame stack', required=False,
|
|
481
|
+
expert=True,
|
|
482
|
+
default=1, min_val=1, max_val=100)
|
|
483
|
+
self.add_field(
|
|
484
|
+
'reference_index', FIELD_REF_IDX, 'Reference frame', required=False,
|
|
485
|
+
expert=True,
|
|
486
|
+
default=0)
|
|
487
|
+
self.add_field(
|
|
488
|
+
'step_process', FIELD_BOOL, 'Step process', required=False,
|
|
489
|
+
expert=True,
|
|
490
|
+
default=True)
|
|
491
|
+
self.add_field(
|
|
492
|
+
'max_threads', FIELD_INT, 'Max num. of cores',
|
|
493
|
+
required=False, default=constants.DEFAULT_MAX_FWK_THREADS,
|
|
494
|
+
expert=True,
|
|
495
|
+
min_val=1, max_val=64)
|
|
496
|
+
self.add_field(
|
|
497
|
+
'chunk_submit', FIELD_BOOL, 'Submit in chunks',
|
|
498
|
+
expert=True,
|
|
499
|
+
required=False, default=constants.DEFAULT_MAX_FWK_CHUNK_SUBMIT)
|
|
547
500
|
|
|
548
501
|
|
|
549
502
|
class MaskNoiseConfigurator(DefaultActionConfigurator):
|
|
@@ -554,13 +507,14 @@ class MaskNoiseConfigurator(DefaultActionConfigurator):
|
|
|
554
507
|
path_type='file', must_exist=True,
|
|
555
508
|
default=constants.DEFAULT_NOISE_MAP_FILENAME,
|
|
556
509
|
placeholder=constants.DEFAULT_NOISE_MAP_FILENAME)
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
510
|
+
self.add_field(
|
|
511
|
+
'kernel_size', FIELD_INT, 'Kernel size', required=False,
|
|
512
|
+
expert=True,
|
|
513
|
+
default=constants.DEFAULT_MN_KERNEL_SIZE, min_val=1, max_val=10)
|
|
514
|
+
self.add_field(
|
|
515
|
+
'method', FIELD_COMBO, 'Interpolation method', required=False,
|
|
516
|
+
expert=True,
|
|
517
|
+
options=['Mean', 'Median'], default='Mean')
|
|
564
518
|
|
|
565
519
|
|
|
566
520
|
class SubsampleActionConfigurator(DefaultActionConfigurator):
|
|
@@ -569,18 +523,23 @@ class SubsampleActionConfigurator(DefaultActionConfigurator):
|
|
|
569
523
|
self.subsample_field = None
|
|
570
524
|
self.fast_subsampling_field = None
|
|
571
525
|
|
|
572
|
-
def add_subsample_fields(self):
|
|
526
|
+
def add_subsample_fields(self, add_to_layout=None):
|
|
527
|
+
if add_to_layout is None:
|
|
528
|
+
add_to_layout = self.builder.main_layout
|
|
573
529
|
self.subsample_field = self.add_field(
|
|
574
530
|
'subsample', FIELD_COMBO, 'Subsample', required=False,
|
|
531
|
+
expert=True,
|
|
575
532
|
options=constants.FIELD_SUBSAMPLE_OPTIONS,
|
|
576
533
|
values=constants.FIELD_SUBSAMPLE_VALUES,
|
|
577
|
-
default=constants.FIELD_SUBSAMPLE_DEFAULT
|
|
534
|
+
default=constants.FIELD_SUBSAMPLE_DEFAULT,
|
|
535
|
+
add_to_layout=add_to_layout)
|
|
578
536
|
self.fast_subsampling_field = self.add_field(
|
|
579
537
|
'fast_subsampling', FIELD_BOOL, 'Fast subsampling', required=False,
|
|
580
|
-
|
|
538
|
+
expert=True,
|
|
539
|
+
default=constants.DEFAULT_ALIGN_FAST_SUBSAMPLING,
|
|
540
|
+
add_to_layout=add_to_layout)
|
|
581
541
|
|
|
582
542
|
self.subsample_field.currentTextChanged.connect(self.change_subsample)
|
|
583
|
-
|
|
584
543
|
self.change_subsample()
|
|
585
544
|
|
|
586
545
|
def change_subsample(self):
|
|
@@ -602,13 +561,14 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
|
|
|
602
561
|
self.detector_field = None
|
|
603
562
|
self.descriptor_field = None
|
|
604
563
|
self.matching_method_field = None
|
|
564
|
+
self.tab_widget = None
|
|
565
|
+
self.current_tab_layout = None
|
|
605
566
|
|
|
606
567
|
def show_info(self, message, timeout=3000):
|
|
607
568
|
self.info_label.setText(message)
|
|
608
|
-
self.info_label.setVisible(True)
|
|
609
569
|
timer = QTimer(self.info_label)
|
|
610
570
|
timer.setSingleShot(True)
|
|
611
|
-
timer.timeout.connect(self.info_label.
|
|
571
|
+
timer.timeout.connect(lambda: self.info_label.setText(''))
|
|
612
572
|
timer.start(timeout)
|
|
613
573
|
|
|
614
574
|
def change_match_config(self):
|
|
@@ -643,158 +603,187 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
|
|
|
643
603
|
self.detector_field = None
|
|
644
604
|
self.descriptor_field = None
|
|
645
605
|
self.matching_method_field = None
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
self.
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
606
|
+
self.tab_widget = self.create_tab_widget(layout)
|
|
607
|
+
feature_layout = self.add_tab(self.tab_widget, "Feature extraction")
|
|
608
|
+
self.create_feature_tab(feature_layout)
|
|
609
|
+
transform_layout = self.add_tab(self.tab_widget, "Transform")
|
|
610
|
+
self.create_transform_tab(transform_layout)
|
|
611
|
+
border_layout = self.add_tab(self.tab_widget, "Border")
|
|
612
|
+
self.create_border_tab(border_layout)
|
|
613
|
+
misc_layout = self.add_tab(self.tab_widget, "Miscellanea")
|
|
614
|
+
self.create_miscellanea_tab(misc_layout)
|
|
615
|
+
|
|
616
|
+
def create_feature_tab(self, layout):
|
|
617
|
+
self.add_bold_label_to_layout(layout, "Feature identification:")
|
|
618
|
+
self.detector_field = self.add_field_to_layout(
|
|
619
|
+
layout, 'detector', FIELD_COMBO, 'Detector', required=False,
|
|
620
|
+
options=constants.VALID_DETECTORS, default=constants.DEFAULT_DETECTOR)
|
|
621
|
+
self.descriptor_field = self.add_field_to_layout(
|
|
622
|
+
layout, 'descriptor', FIELD_COMBO, 'Descriptor', required=False,
|
|
623
|
+
options=constants.VALID_DESCRIPTORS, default=constants.DEFAULT_DESCRIPTOR)
|
|
624
|
+
self.detector_field.setToolTip(
|
|
625
|
+
"SIFT: Requires SIFT descriptor and K-NN matching\n"
|
|
626
|
+
"ORB/AKAZE: Work best with Hamming distance"
|
|
627
|
+
)
|
|
628
|
+
self.descriptor_field.setToolTip(
|
|
629
|
+
"SIFT: Requires K-NN matching\n"
|
|
630
|
+
"ORB/AKAZE: Require Hamming distance with ORB/AKAZE detectors"
|
|
631
|
+
)
|
|
632
|
+
self.detector_field.currentIndexChanged.connect(self.change_match_config)
|
|
633
|
+
self.descriptor_field.currentIndexChanged.connect(self.change_match_config)
|
|
634
|
+
self.info_label = QLabel()
|
|
635
|
+
self.info_label.setStyleSheet("color: orange; font-style: italic;")
|
|
636
|
+
layout.addRow(self.info_label)
|
|
637
|
+
self.add_bold_label_to_layout(layout, "Feature matching:")
|
|
638
|
+
self.matching_method_field = self.add_field_to_layout(
|
|
639
|
+
layout, 'match_method', FIELD_COMBO, 'Match method', required=False,
|
|
640
|
+
options=self.MATCHING_METHOD_OPTIONS, values=constants.VALID_MATCHING_METHODS,
|
|
641
|
+
default=constants.DEFAULT_MATCHING_METHOD)
|
|
642
|
+
self.matching_method_field.setToolTip(
|
|
643
|
+
"Automatically selected based on detector/descriptor combination"
|
|
644
|
+
)
|
|
645
|
+
self.matching_method_field.currentIndexChanged.connect(self.change_match_config)
|
|
646
|
+
self.add_field_to_layout(
|
|
647
|
+
layout, 'flann_idx_kdtree', FIELD_INT, 'Flann idx kdtree', required=False,
|
|
648
|
+
expert=True,
|
|
649
|
+
default=constants.DEFAULT_FLANN_IDX_KDTREE,
|
|
650
|
+
min_val=0, max_val=10)
|
|
651
|
+
self.add_field_to_layout(
|
|
652
|
+
layout, 'flann_trees', FIELD_INT, 'Flann trees', required=False,
|
|
653
|
+
expert=True,
|
|
654
|
+
default=constants.DEFAULT_FLANN_TREES,
|
|
655
|
+
min_val=0, max_val=10)
|
|
656
|
+
self.add_field_to_layout(
|
|
657
|
+
layout, 'flann_checks', FIELD_INT, 'Flann checks', required=False,
|
|
658
|
+
expert=True,
|
|
659
|
+
default=constants.DEFAULT_FLANN_CHECKS,
|
|
660
|
+
min_val=0, max_val=1000)
|
|
661
|
+
self.add_field_to_layout(
|
|
662
|
+
layout, 'threshold', FIELD_FLOAT, 'Threshold', required=False,
|
|
663
|
+
expert=True,
|
|
664
|
+
default=constants.DEFAULT_ALIGN_THRESHOLD,
|
|
665
|
+
min_val=0, max_val=1, step=0.05)
|
|
666
|
+
self.add_subsample_fields(add_to_layout=layout)
|
|
667
|
+
|
|
668
|
+
def create_transform_tab(self, layout):
|
|
669
|
+
self.add_bold_label_to_layout(layout, "Transform:")
|
|
670
|
+
transform = self.add_field_to_layout(
|
|
671
|
+
layout, 'transform', FIELD_COMBO, 'Transform', required=False,
|
|
672
|
+
options=self.TRANSFORM_OPTIONS, values=constants.VALID_TRANSFORMS,
|
|
673
|
+
default=constants.DEFAULT_TRANSFORM)
|
|
674
|
+
method = self.add_field_to_layout(
|
|
675
|
+
layout, 'align_method', FIELD_COMBO, 'Align method', required=False,
|
|
676
|
+
options=self.METHOD_OPTIONS, values=constants.VALID_ALIGN_METHODS,
|
|
677
|
+
default=constants.DEFAULT_ALIGN_METHOD)
|
|
678
|
+
rans_threshold = self.add_field_to_layout(
|
|
679
|
+
layout, 'rans_threshold', FIELD_FLOAT, 'RANSAC threshold (px)', required=False,
|
|
680
|
+
expert=True,
|
|
681
|
+
default=constants.DEFAULT_RANS_THRESHOLD, min_val=0, max_val=20, step=0.1)
|
|
682
|
+
self.add_field_to_layout(
|
|
683
|
+
layout, 'min_good_matches', FIELD_INT, "Min. good matches", required=False,
|
|
684
|
+
expert=True,
|
|
685
|
+
default=constants.DEFAULT_ALIGN_MIN_GOOD_MATCHES, min_val=0, max_val=500)
|
|
686
|
+
|
|
687
|
+
def change_method():
|
|
688
|
+
text = method.currentText()
|
|
689
|
+
if text == self.METHOD_OPTIONS[0]:
|
|
690
|
+
rans_threshold.setEnabled(True)
|
|
691
|
+
elif text == self.METHOD_OPTIONS[1]:
|
|
692
|
+
rans_threshold.setEnabled(False)
|
|
693
|
+
|
|
694
|
+
method.currentIndexChanged.connect(change_method)
|
|
695
|
+
change_method()
|
|
696
|
+
self.add_field_to_layout(
|
|
697
|
+
layout, 'align_confidence', FIELD_FLOAT, 'Confidence (%)',
|
|
698
|
+
required=False, decimals=1,
|
|
699
|
+
expert=True,
|
|
700
|
+
default=constants.DEFAULT_ALIGN_CONFIDENCE,
|
|
701
|
+
min_val=70.0, max_val=100.0, step=0.1)
|
|
702
|
+
refine_iters = self.add_field_to_layout(
|
|
703
|
+
layout, 'refine_iters', FIELD_INT, 'Refinement iterations (Rigid)', required=False,
|
|
704
|
+
expert=True,
|
|
705
|
+
default=constants.DEFAULT_REFINE_ITERS, min_val=0, max_val=1000)
|
|
706
|
+
max_iters = self.add_field_to_layout(
|
|
707
|
+
layout, 'max_iters', FIELD_INT, 'Max. iterations (Homography)', required=False,
|
|
708
|
+
expert=True,
|
|
709
|
+
default=constants.DEFAULT_ALIGN_MAX_ITERS, min_val=0, max_val=5000)
|
|
710
|
+
|
|
711
|
+
def change_transform():
|
|
712
|
+
text = transform.currentText()
|
|
713
|
+
if text == self.TRANSFORM_OPTIONS[0]:
|
|
714
|
+
refine_iters.setEnabled(True)
|
|
715
|
+
max_iters.setEnabled(False)
|
|
716
|
+
elif text == self.TRANSFORM_OPTIONS[1]:
|
|
717
|
+
refine_iters.setEnabled(False)
|
|
718
|
+
max_iters.setEnabled(True)
|
|
719
|
+
|
|
720
|
+
transform.currentIndexChanged.connect(change_transform)
|
|
721
|
+
change_transform()
|
|
722
|
+
self.add_field_to_layout(
|
|
723
|
+
layout, 'abort_abnormal', FIELD_BOOL, 'Abort on abnormal transf.',
|
|
724
|
+
expert=True,
|
|
725
|
+
required=False, default=constants.DEFAULT_ALIGN_ABORT_ABNORMAL)
|
|
726
|
+
|
|
727
|
+
def create_border_tab(self, layout):
|
|
728
|
+
self.add_bold_label_to_layout(layout, "Border:")
|
|
729
|
+
self.add_field_to_layout(
|
|
730
|
+
layout, 'border_mode', FIELD_COMBO, 'Border mode', required=False,
|
|
731
|
+
options=self.BORDER_MODE_OPTIONS,
|
|
732
|
+
values=constants.VALID_BORDER_MODES,
|
|
733
|
+
default=constants.DEFAULT_BORDER_MODE)
|
|
734
|
+
self.add_field_to_layout(
|
|
735
|
+
layout, 'border_value', FIELD_INT_TUPLE,
|
|
736
|
+
'Border value (if constant)', required=False, size=4,
|
|
737
|
+
expert=True,
|
|
738
|
+
default=constants.DEFAULT_BORDER_VALUE,
|
|
739
|
+
labels=constants.RGBA_LABELS,
|
|
740
|
+
min_val=constants.DEFAULT_BORDER_VALUE, max_val=[255] * 4)
|
|
741
|
+
self.add_field_to_layout(
|
|
742
|
+
layout, 'border_blur', FIELD_FLOAT, 'Border blur', required=False,
|
|
743
|
+
expert=True,
|
|
744
|
+
default=constants.DEFAULT_BORDER_BLUR,
|
|
745
|
+
min_val=0, max_val=1000, step=1)
|
|
746
|
+
|
|
747
|
+
def create_miscellanea_tab(self, layout):
|
|
748
|
+
self.add_bold_label_to_layout(layout, "Miscellanea:")
|
|
749
|
+
mode = self.add_field_to_layout(
|
|
750
|
+
layout, 'mode', FIELD_COMBO, 'Mode',
|
|
751
|
+
required=False, options=self.MODE_OPTIONS, values=constants.ALIGN_VALID_MODES,
|
|
752
|
+
default=dict(zip(constants.ALIGN_VALID_MODES,
|
|
753
|
+
self.MODE_OPTIONS))[constants.DEFAULT_ALIGN_MODE])
|
|
754
|
+
memory_limit = self.add_field_to_layout(
|
|
755
|
+
layout, 'memory_limit', FIELD_FLOAT, 'Memory limit (approx., GBytes)',
|
|
756
|
+
required=False, default=constants.DEFAULT_ALIGN_MEMORY_LIMIT_GB,
|
|
757
|
+
min_val=1.0, max_val=64.0)
|
|
758
|
+
max_threads = self.add_field_to_layout(
|
|
759
|
+
layout, 'max_threads', FIELD_INT, 'Max num. of cores',
|
|
760
|
+
required=False, default=constants.DEFAULT_ALIGN_MAX_THREADS,
|
|
761
|
+
min_val=1, max_val=64)
|
|
762
|
+
chunk_submit = self.add_field_to_layout(
|
|
763
|
+
layout, 'chunk_submit', FIELD_BOOL, 'Submit in chunks',
|
|
764
|
+
expert=True,
|
|
765
|
+
required=False, default=constants.DEFAULT_ALIGN_CHUNK_SUBMIT)
|
|
766
|
+
bw_matching = self.add_field_to_layout(
|
|
767
|
+
layout, 'bw_matching', FIELD_BOOL, 'Match using black & white',
|
|
768
|
+
expert=True,
|
|
769
|
+
required=False, default=constants.DEFAULT_ALIGN_BW_MATCHING)
|
|
770
|
+
|
|
771
|
+
def change_mode():
|
|
772
|
+
text = mode.currentText()
|
|
773
|
+
enabled = text != self.MODE_OPTIONS[1]
|
|
774
|
+
memory_limit.setEnabled(enabled)
|
|
775
|
+
max_threads.setEnabled(enabled)
|
|
776
|
+
chunk_submit.setEnabled(enabled)
|
|
777
|
+
bw_matching.setEnabled(enabled)
|
|
778
|
+
|
|
779
|
+
mode.currentIndexChanged.connect(change_mode)
|
|
780
|
+
|
|
781
|
+
self.add_field_to_layout(
|
|
782
|
+
layout, 'plot_summary', FIELD_BOOL, 'Plot summary',
|
|
795
783
|
required=False, default=False)
|
|
796
|
-
|
|
797
|
-
|
|
784
|
+
|
|
785
|
+
self.add_field_to_layout(
|
|
786
|
+
layout, 'plot_matches', FIELD_BOOL, 'Plot matches',
|
|
798
787
|
required=False, default=False)
|
|
799
788
|
|
|
800
789
|
def update_params(self, params):
|
|
@@ -821,16 +810,17 @@ class BalanceFramesConfigurator(SubsampleActionConfigurator):
|
|
|
821
810
|
|
|
822
811
|
def create_form(self, layout, action):
|
|
823
812
|
super().create_form(layout, action)
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
813
|
+
self.add_field(
|
|
814
|
+
'mask_size', FIELD_FLOAT, 'Mask size', required=False,
|
|
815
|
+
expert=True,
|
|
816
|
+
default=0, min_val=0, max_val=5, step=0.1)
|
|
817
|
+
self.add_field(
|
|
818
|
+
'intensity_interval', FIELD_INT_TUPLE, 'Intensity range',
|
|
819
|
+
required=False, size=2,
|
|
820
|
+
expert=True,
|
|
821
|
+
default=[v for k, v in constants.DEFAULT_INTENSITY_INTERVAL.items()],
|
|
822
|
+
labels=['min', 'max'], min_val=[-1] * 2, max_val=[65536] * 2)
|
|
823
|
+
self.add_subsample_fields()
|
|
834
824
|
self.add_field(
|
|
835
825
|
'corr_map', FIELD_COMBO, 'Correction map', required=False,
|
|
836
826
|
options=self.CORRECTION_MAP_OPTIONS, values=constants.VALID_BALANCE,
|
|
@@ -851,15 +841,16 @@ class BalanceFramesConfigurator(SubsampleActionConfigurator):
|
|
|
851
841
|
class VignettingConfigurator(SubsampleActionConfigurator):
|
|
852
842
|
def create_form(self, layout, action):
|
|
853
843
|
super().create_form(layout, action)
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
844
|
+
self.add_field(
|
|
845
|
+
'r_steps', FIELD_INT, 'Radial steps', required=False,
|
|
846
|
+
expert=True,
|
|
847
|
+
default=constants.DEFAULT_R_STEPS, min_val=1, max_val=1000)
|
|
848
|
+
self.add_field(
|
|
849
|
+
'black_threshold', FIELD_INT, 'Black intensity threshold',
|
|
850
|
+
expert=True,
|
|
851
|
+
required=False, default=constants.DEFAULT_BLACK_THRESHOLD,
|
|
852
|
+
min_val=0, max_val=1000)
|
|
853
|
+
self.add_subsample_fields()
|
|
863
854
|
self.add_field(
|
|
864
855
|
'max_correction', FIELD_FLOAT, 'Max. correction', required=False,
|
|
865
856
|
default=constants.DEFAULT_MAX_CORRECTION,
|