shinestacker 1.2.1__py3-none-any.whl → 1.3.1__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 +152 -112
- shinestacker/algorithms/align_auto.py +76 -0
- shinestacker/algorithms/align_parallel.py +336 -0
- shinestacker/algorithms/balance.py +3 -1
- shinestacker/algorithms/base_stack_algo.py +25 -22
- shinestacker/algorithms/depth_map.py +9 -14
- shinestacker/algorithms/multilayer.py +8 -8
- shinestacker/algorithms/noise_detection.py +10 -10
- shinestacker/algorithms/pyramid.py +10 -24
- shinestacker/algorithms/pyramid_auto.py +21 -24
- shinestacker/algorithms/pyramid_tiles.py +31 -25
- shinestacker/algorithms/stack.py +21 -17
- shinestacker/algorithms/stack_framework.py +98 -47
- shinestacker/algorithms/utils.py +16 -0
- shinestacker/algorithms/vignetting.py +13 -10
- shinestacker/app/gui_utils.py +10 -0
- shinestacker/app/main.py +10 -4
- shinestacker/app/project.py +3 -1
- shinestacker/app/retouch.py +3 -1
- shinestacker/config/constants.py +60 -25
- shinestacker/config/gui_constants.py +1 -1
- shinestacker/core/core_utils.py +4 -0
- shinestacker/core/framework.py +104 -23
- shinestacker/gui/action_config.py +4 -5
- shinestacker/gui/action_config_dialog.py +409 -239
- shinestacker/gui/base_form_dialog.py +2 -2
- shinestacker/gui/colors.py +1 -0
- shinestacker/gui/folder_file_selection.py +106 -0
- shinestacker/gui/gui_run.py +12 -10
- shinestacker/gui/main_window.py +10 -5
- shinestacker/gui/new_project.py +171 -73
- shinestacker/gui/project_controller.py +10 -6
- shinestacker/gui/project_converter.py +4 -2
- shinestacker/gui/project_editor.py +40 -28
- shinestacker/gui/select_path_widget.py +1 -1
- shinestacker/gui/sys_mon.py +97 -0
- shinestacker/gui/time_progress_bar.py +4 -3
- shinestacker/retouch/exif_data.py +1 -1
- shinestacker/retouch/image_editor_ui.py +2 -0
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.1.dist-info}/METADATA +6 -6
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.1.dist-info}/RECORD +46 -42
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.1.dist-info}/WHEEL +0 -0
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.1.dist-info}/entry_points.txt +0 -0
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.1.dist-info}/licenses/LICENSE +0 -0
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.1.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, E0611, R0913, R0917, R0915, R0912
|
|
2
|
-
# pylint: disable=E0606, W0718, R1702, W0102, W0221
|
|
2
|
+
# pylint: disable=E0606, W0718, R1702, W0102, W0221, R0914
|
|
3
|
+
import os
|
|
3
4
|
import traceback
|
|
4
|
-
from
|
|
5
|
-
|
|
6
|
-
QMessageBox, QStackedWidget, QFormLayout, QDialog)
|
|
5
|
+
from PySide6.QtWidgets import (QWidget, QPushButton, QHBoxLayout, QLabel, QScrollArea, QSizePolicy,
|
|
6
|
+
QMessageBox, QStackedWidget, QFormLayout, QDialog, QTabWidget)
|
|
7
7
|
from PySide6.QtCore import Qt, QTimer
|
|
8
8
|
from .. config.constants import constants
|
|
9
9
|
from .. algorithms.align import validate_align_config
|
|
@@ -14,6 +14,7 @@ from . action_config import (
|
|
|
14
14
|
FIELD_TEXT, FIELD_ABS_PATH, FIELD_REL_PATH, FIELD_FLOAT,
|
|
15
15
|
FIELD_INT, FIELD_INT_TUPLE, FIELD_BOOL, FIELD_COMBO, FIELD_REF_IDX
|
|
16
16
|
)
|
|
17
|
+
from .folder_file_selection import FolderFileSelectionWidget
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class ActionConfigDialog(QDialog):
|
|
@@ -104,8 +105,6 @@ class ActionConfigDialog(QDialog):
|
|
|
104
105
|
if self.configurator.update_params(self.action.params):
|
|
105
106
|
self.parent().mark_as_modified(True, "Modify Configuration")
|
|
106
107
|
super().accept()
|
|
107
|
-
else:
|
|
108
|
-
self.parent().project_editor.pop_undo()
|
|
109
108
|
|
|
110
109
|
def reset_to_defaults(self):
|
|
111
110
|
builder = self.configurator.get_builder()
|
|
@@ -124,7 +123,7 @@ class NoNameActionConfigurator(ActionConfigurator):
|
|
|
124
123
|
def get_builder(self):
|
|
125
124
|
return self.builder
|
|
126
125
|
|
|
127
|
-
def update_params(self, params
|
|
126
|
+
def update_params(self, params):
|
|
128
127
|
return self.builder.update_params(params)
|
|
129
128
|
|
|
130
129
|
def add_bold_label(self, label):
|
|
@@ -139,6 +138,49 @@ class NoNameActionConfigurator(ActionConfigurator):
|
|
|
139
138
|
required=False, add_to_layout=None, **kwargs):
|
|
140
139
|
return self.builder.add_field(tag, field_type, label, required, add_to_layout, **kwargs)
|
|
141
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
|
+
def create_tab_widget(self, layout):
|
|
160
|
+
tab_widget = QTabWidget()
|
|
161
|
+
layout.addRow(tab_widget)
|
|
162
|
+
return tab_widget
|
|
163
|
+
|
|
164
|
+
def add_tab(self, tab_widget, title):
|
|
165
|
+
tab = QWidget()
|
|
166
|
+
tab_layout = QFormLayout()
|
|
167
|
+
tab_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
|
|
168
|
+
tab_layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
|
|
169
|
+
tab_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)
|
|
170
|
+
tab_layout.setLabelAlignment(Qt.AlignLeft)
|
|
171
|
+
tab.setLayout(tab_layout)
|
|
172
|
+
tab_widget.addTab(tab, title)
|
|
173
|
+
return tab_layout
|
|
174
|
+
|
|
175
|
+
def add_field_to_layout(self, layout, tag, field_type, label, required=False, **kwargs):
|
|
176
|
+
return self.add_field(tag, field_type, label, required, add_to_layout=layout, **kwargs)
|
|
177
|
+
|
|
178
|
+
def add_bold_label_to_layout(self, layout, label):
|
|
179
|
+
label_widget = QLabel(label)
|
|
180
|
+
label_widget.setStyleSheet("font-weight: bold")
|
|
181
|
+
layout.addRow(label_widget)
|
|
182
|
+
return label_widget
|
|
183
|
+
|
|
142
184
|
|
|
143
185
|
class DefaultActionConfigurator(NoNameActionConfigurator):
|
|
144
186
|
def create_form(self, layout, action, tag='Action'):
|
|
@@ -148,13 +190,85 @@ class DefaultActionConfigurator(NoNameActionConfigurator):
|
|
|
148
190
|
|
|
149
191
|
|
|
150
192
|
class JobConfigurator(DefaultActionConfigurator):
|
|
193
|
+
def __init__(self, expert, current_wd):
|
|
194
|
+
super().__init__(expert, current_wd)
|
|
195
|
+
self.working_path_label = None
|
|
196
|
+
self.input_path_label = None
|
|
197
|
+
self.frames_label = None
|
|
198
|
+
self.input_widget = None
|
|
199
|
+
|
|
151
200
|
def create_form(self, layout, action):
|
|
152
201
|
super().create_form(layout, action, "Job")
|
|
153
|
-
self.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
202
|
+
self.input_widget = FolderFileSelectionWidget()
|
|
203
|
+
self.frames_label = QLabel("0")
|
|
204
|
+
working_path = action.params.get('working_path', '')
|
|
205
|
+
input_path = action.params.get('input_path', '')
|
|
206
|
+
input_filepaths = action.params.get('input_filepaths', [])
|
|
207
|
+
if isinstance(input_filepaths, str) and input_filepaths:
|
|
208
|
+
input_filepaths = input_filepaths.split(constants.PATH_SEPARATOR)
|
|
209
|
+
self.working_path_label = QLabel(working_path or "Not set")
|
|
210
|
+
self.input_path_label = QLabel(input_path or "Not set")
|
|
211
|
+
self.input_widget.path_edit.setText('')
|
|
212
|
+
if input_filepaths:
|
|
213
|
+
self.input_widget.files_mode_radio.setChecked(True)
|
|
214
|
+
else:
|
|
215
|
+
self.input_widget.folder_mode_radio.setChecked(False)
|
|
216
|
+
self.input_widget.text_changed_connect(self.update_paths_and_frames)
|
|
217
|
+
self.input_widget.folder_mode_radio.toggled.connect(self.update_paths_and_frames)
|
|
218
|
+
self.input_widget.files_mode_radio.toggled.connect(self.update_paths_and_frames)
|
|
219
|
+
self.add_bold_label("Input Selection:")
|
|
220
|
+
self.add_row(self.input_widget)
|
|
221
|
+
self.add_labelled_row("Number of frames: ", self.frames_label)
|
|
222
|
+
self.add_bold_label("Derived Paths:")
|
|
223
|
+
self.add_labelled_row("Working path: ", self.working_path_label)
|
|
224
|
+
self.add_labelled_row("Input path:", self.input_path_label)
|
|
225
|
+
self.set_paths_and_frames(working_path, input_path, input_filepaths)
|
|
226
|
+
|
|
227
|
+
def update_frames_count(self):
|
|
228
|
+
if self.input_widget.get_selection_mode() == 'files':
|
|
229
|
+
count = self.input_widget.num_selected_files()
|
|
230
|
+
else:
|
|
231
|
+
count = self.count_image_files(self.input_widget.get_path())
|
|
232
|
+
self.frames_label.setText(str(count))
|
|
233
|
+
|
|
234
|
+
def set_paths_and_frames(self, working_path, input_path, input_filepaths):
|
|
235
|
+
self.input_path_label.setText(input_path or "Not set")
|
|
236
|
+
self.working_path_label.setText(working_path or "Not set")
|
|
237
|
+
self.frames_label.setText(str(len(input_filepaths)))
|
|
238
|
+
|
|
239
|
+
def update_paths_and_frames(self, ):
|
|
240
|
+
input_fullpath = self.input_widget.get_path()
|
|
241
|
+
input_path = os.path.basename(os.path.normpath(input_fullpath)) if input_fullpath else ""
|
|
242
|
+
working_path = os.path.dirname(input_fullpath) if input_fullpath else ""
|
|
243
|
+
self.input_path_label.setText(input_path or "Not set")
|
|
244
|
+
self.working_path_label.setText(working_path or "Not set")
|
|
245
|
+
self.update_frames_count()
|
|
246
|
+
|
|
247
|
+
def count_image_files(self, path):
|
|
248
|
+
if not path or not os.path.isdir(path):
|
|
249
|
+
return 0
|
|
250
|
+
count = 0
|
|
251
|
+
for filename in os.listdir(path):
|
|
252
|
+
if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.tif', '.tiff')):
|
|
253
|
+
count += 1
|
|
254
|
+
return count
|
|
255
|
+
|
|
256
|
+
def update_params(self, params):
|
|
257
|
+
if not super().update_params(params):
|
|
258
|
+
return False
|
|
259
|
+
selection_mode = self.input_widget.get_selection_mode()
|
|
260
|
+
selected_files = self.input_widget.get_selected_files()
|
|
261
|
+
if selection_mode == 'files' and selected_files:
|
|
262
|
+
input_full_path = os.path.dirname(selected_files[0])
|
|
263
|
+
params['input_filepaths'] = self.input_widget.get_selected_filenames()
|
|
264
|
+
else:
|
|
265
|
+
input_full_path = self.input_widget.get_path()
|
|
266
|
+
params['input_filepaths'] = []
|
|
267
|
+
input_path = os.path.basename(os.path.normpath(input_full_path)) if input_full_path else ""
|
|
268
|
+
working_path = os.path.dirname(input_full_path) if input_full_path else ""
|
|
269
|
+
params['input_path'] = input_path
|
|
270
|
+
params['working_path'] = working_path
|
|
271
|
+
return True
|
|
158
272
|
|
|
159
273
|
|
|
160
274
|
class NoiseDetectionConfigurator(DefaultActionConfigurator):
|
|
@@ -205,38 +319,48 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
|
|
|
205
319
|
FLOAT_OPTIONS = ['float 32 bits', 'float 64 bits']
|
|
206
320
|
MODE_OPTIONS = ['Auto', 'All in memory', 'Tiled I/O buffered']
|
|
207
321
|
|
|
322
|
+
def __init__(self, expert, current_wd):
|
|
323
|
+
super().__init__(expert, current_wd)
|
|
324
|
+
self.tab_widget = None
|
|
325
|
+
self.general_tab_layout = None
|
|
326
|
+
self.algorithm_tab_layout = None
|
|
327
|
+
|
|
208
328
|
def create_form(self, layout, action):
|
|
209
329
|
super().create_form(layout, action)
|
|
330
|
+
self.tab_widget = self.create_tab_widget(layout)
|
|
331
|
+
self.general_tab_layout = self.add_tab(self.tab_widget, "General Parameters")
|
|
332
|
+
self.create_general_tab(self.general_tab_layout)
|
|
333
|
+
self.algorithm_tab_layout = self.add_tab(self.tab_widget, "Stacking Algorithm")
|
|
334
|
+
self.create_algorithm_tab(self.algorithm_tab_layout)
|
|
335
|
+
|
|
336
|
+
def create_general_tab(self, layout):
|
|
210
337
|
if self.expert:
|
|
211
|
-
self.
|
|
212
|
-
'working_path', FIELD_ABS_PATH, 'Working path', required=False)
|
|
213
|
-
self.
|
|
214
|
-
'input_path', FIELD_REL_PATH, 'Input path', required=False,
|
|
338
|
+
self.add_field_to_layout(
|
|
339
|
+
layout, 'working_path', FIELD_ABS_PATH, 'Working path', required=False)
|
|
340
|
+
self.add_field_to_layout(
|
|
341
|
+
layout, 'input_path', FIELD_REL_PATH, 'Input path', required=False,
|
|
215
342
|
placeholder='relative to working path')
|
|
216
|
-
self.
|
|
217
|
-
'output_path', FIELD_REL_PATH, 'Output path', required=False,
|
|
343
|
+
self.add_field_to_layout(
|
|
344
|
+
layout, 'output_path', FIELD_REL_PATH, 'Output path', required=False,
|
|
218
345
|
placeholder='relative to working path')
|
|
219
|
-
self.
|
|
220
|
-
'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
|
|
346
|
+
self.add_field_to_layout(
|
|
347
|
+
layout, 'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
|
|
221
348
|
required=False, default=True)
|
|
222
349
|
|
|
223
|
-
def
|
|
224
|
-
self.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
self.add_bold_label("Stacking algorithm:")
|
|
228
|
-
combo = self.add_field(
|
|
229
|
-
'stacker', FIELD_COMBO, 'Stacking algorithm', required=True,
|
|
350
|
+
def create_algorithm_tab(self, layout):
|
|
351
|
+
self.add_bold_label_to_layout(layout, "Stacking algorithm:")
|
|
352
|
+
combo = self.add_field_to_layout(
|
|
353
|
+
layout, 'stacker', FIELD_COMBO, 'Stacking algorithm', required=True,
|
|
230
354
|
options=constants.STACK_ALGO_OPTIONS,
|
|
231
355
|
default=constants.STACK_ALGO_DEFAULT)
|
|
232
356
|
q_pyramid, q_depthmap = QWidget(), QWidget()
|
|
233
357
|
for q in [q_pyramid, q_depthmap]:
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
q.setLayout(
|
|
358
|
+
tab_layout = QFormLayout()
|
|
359
|
+
tab_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
|
|
360
|
+
tab_layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
|
|
361
|
+
tab_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)
|
|
362
|
+
tab_layout.setLabelAlignment(Qt.AlignLeft)
|
|
363
|
+
q.setLayout(tab_layout)
|
|
240
364
|
stacked = QStackedWidget()
|
|
241
365
|
stacked.addWidget(q_pyramid)
|
|
242
366
|
stacked.addWidget(q_depthmap)
|
|
@@ -247,52 +371,45 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
|
|
|
247
371
|
stacked.setCurrentWidget(q_pyramid)
|
|
248
372
|
elif text == constants.STACK_ALGO_DEPTH_MAP:
|
|
249
373
|
stacked.setCurrentWidget(q_depthmap)
|
|
374
|
+
|
|
250
375
|
change()
|
|
251
376
|
if self.expert:
|
|
252
|
-
self.
|
|
253
|
-
'pyramid_min_size', FIELD_INT, 'Minimum size (px)',
|
|
254
|
-
required=False,
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
'pyramid_gen_kernel', FIELD_FLOAT, 'Gen. kernel',
|
|
262
|
-
required=False, add_to_layout=q_pyramid.layout(),
|
|
263
|
-
default=constants.DEFAULT_PY_GEN_KERNEL,
|
|
377
|
+
self.add_field_to_layout(
|
|
378
|
+
q_pyramid.layout(), 'pyramid_min_size', FIELD_INT, 'Minimum size (px)',
|
|
379
|
+
required=False, default=constants.DEFAULT_PY_MIN_SIZE, min_val=2, max_val=256)
|
|
380
|
+
self.add_field_to_layout(
|
|
381
|
+
q_pyramid.layout(), 'pyramid_kernel_size', FIELD_INT, 'Kernel size (px)',
|
|
382
|
+
required=False, default=constants.DEFAULT_PY_KERNEL_SIZE, min_val=3, max_val=21)
|
|
383
|
+
self.add_field_to_layout(
|
|
384
|
+
q_pyramid.layout(), 'pyramid_gen_kernel', FIELD_FLOAT, 'Gen. kernel',
|
|
385
|
+
required=False, default=constants.DEFAULT_PY_GEN_KERNEL,
|
|
264
386
|
min_val=0.0, max_val=2.0)
|
|
265
|
-
self.
|
|
266
|
-
'pyramid_float_type', FIELD_COMBO, 'Precision', required=False,
|
|
267
|
-
add_to_layout=q_pyramid.layout(),
|
|
387
|
+
self.add_field_to_layout(
|
|
388
|
+
q_pyramid.layout(), 'pyramid_float_type', FIELD_COMBO, 'Precision', required=False,
|
|
268
389
|
options=self.FLOAT_OPTIONS, values=constants.VALID_FLOATS,
|
|
269
390
|
default=dict(zip(constants.VALID_FLOATS,
|
|
270
391
|
self.FLOAT_OPTIONS))[constants.DEFAULT_PY_FLOAT])
|
|
271
|
-
mode = self.
|
|
272
|
-
'pyramid_mode', FIELD_COMBO, 'Mode',
|
|
273
|
-
required=False,
|
|
274
|
-
options=self.MODE_OPTIONS, values=constants.PY_VALID_MODES,
|
|
392
|
+
mode = self.add_field_to_layout(
|
|
393
|
+
q_pyramid.layout(), 'pyramid_mode', FIELD_COMBO, 'Mode',
|
|
394
|
+
required=False, options=self.MODE_OPTIONS, values=constants.PY_VALID_MODES,
|
|
275
395
|
default=dict(zip(constants.PY_VALID_MODES,
|
|
276
396
|
self.MODE_OPTIONS))[constants.DEFAULT_PY_MODE])
|
|
277
|
-
memory_limit = self.
|
|
278
|
-
'pyramid_memory_limit', FIELD_FLOAT,
|
|
279
|
-
|
|
280
|
-
default=constants.DEFAULT_PY_MEMORY_LIMIT_GB,
|
|
397
|
+
memory_limit = self.add_field_to_layout(
|
|
398
|
+
q_pyramid.layout(), 'pyramid_memory_limit', FIELD_FLOAT,
|
|
399
|
+
'Memory limit (approx., GBytes)',
|
|
400
|
+
required=False, default=constants.DEFAULT_PY_MEMORY_LIMIT_GB,
|
|
281
401
|
min_val=1.0, max_val=64.0)
|
|
282
|
-
max_threads = self.
|
|
283
|
-
'pyramid_max_threads', FIELD_INT, 'Max num. of cores',
|
|
284
|
-
required=False,
|
|
285
|
-
default=constants.DEFAULT_PY_MAX_THREADS,
|
|
402
|
+
max_threads = self.add_field_to_layout(
|
|
403
|
+
q_pyramid.layout(), 'pyramid_max_threads', FIELD_INT, 'Max num. of cores',
|
|
404
|
+
required=False, default=constants.DEFAULT_PY_MAX_THREADS,
|
|
286
405
|
min_val=1, max_val=64)
|
|
287
|
-
tile_size = self.
|
|
288
|
-
'pyramid_tile_size', FIELD_INT, 'Tile size (px)',
|
|
289
|
-
required=False,
|
|
290
|
-
default=constants.DEFAULT_PY_TILE_SIZE,
|
|
406
|
+
tile_size = self.add_field_to_layout(
|
|
407
|
+
q_pyramid.layout(), 'pyramid_tile_size', FIELD_INT, 'Tile size (px)',
|
|
408
|
+
required=False, default=constants.DEFAULT_PY_TILE_SIZE,
|
|
291
409
|
min_val=128, max_val=2048)
|
|
292
|
-
n_tiled_layers = self.
|
|
293
|
-
'pyramid_n_tiled_layers', FIELD_INT, 'Num. tiled layers',
|
|
294
|
-
required=False,
|
|
295
|
-
default=constants.DEFAULT_PY_N_TILED_LAYERS,
|
|
410
|
+
n_tiled_layers = self.add_field_to_layout(
|
|
411
|
+
q_pyramid.layout(), 'pyramid_n_tiled_layers', FIELD_INT, 'Num. tiled layers',
|
|
412
|
+
required=False, default=constants.DEFAULT_PY_N_TILED_LAYERS,
|
|
296
413
|
min_val=0, max_val=6)
|
|
297
414
|
|
|
298
415
|
def change_mode():
|
|
@@ -305,47 +422,41 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
|
|
|
305
422
|
|
|
306
423
|
mode.currentIndexChanged.connect(change_mode)
|
|
307
424
|
change_mode()
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
425
|
+
|
|
426
|
+
self.add_field_to_layout(
|
|
427
|
+
q_depthmap.layout(), 'depthmap_energy', FIELD_COMBO, 'Energy', required=False,
|
|
311
428
|
options=self.ENERGY_OPTIONS, values=constants.VALID_DM_ENERGY,
|
|
312
429
|
default=dict(zip(constants.VALID_DM_ENERGY,
|
|
313
430
|
self.ENERGY_OPTIONS))[constants.DEFAULT_DM_ENERGY])
|
|
314
|
-
self.
|
|
315
|
-
'map_type', FIELD_COMBO, 'Map type', required=False,
|
|
316
|
-
add_to_layout=q_depthmap.layout(),
|
|
431
|
+
self.add_field_to_layout(
|
|
432
|
+
q_depthmap.layout(), 'map_type', FIELD_COMBO, 'Map type', required=False,
|
|
317
433
|
options=self.MAP_TYPE_OPTIONS, values=constants.VALID_DM_MAP,
|
|
318
434
|
default=dict(zip(constants.VALID_DM_MAP,
|
|
319
435
|
self.MAP_TYPE_OPTIONS))[constants.DEFAULT_DM_MAP])
|
|
320
436
|
if self.expert:
|
|
321
|
-
self.
|
|
322
|
-
'depthmap_kernel_size', FIELD_INT, 'Kernel size (px)',
|
|
323
|
-
required=False,
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
default=constants.
|
|
333
|
-
self.add_field(
|
|
334
|
-
'depthmap_temperature', FIELD_FLOAT, 'Temperature',
|
|
335
|
-
required=False, add_to_layout=q_depthmap.layout(),
|
|
336
|
-
default=constants.DEFAULT_DM_TEMPERATURE,
|
|
437
|
+
self.add_field_to_layout(
|
|
438
|
+
q_depthmap.layout(), 'depthmap_kernel_size', FIELD_INT, 'Kernel size (px)',
|
|
439
|
+
required=False, default=constants.DEFAULT_DM_KERNEL_SIZE, min_val=3, max_val=21)
|
|
440
|
+
self.add_field_to_layout(
|
|
441
|
+
q_depthmap.layout(), 'depthmap_blur_size', FIELD_INT, 'Blurl size (px)',
|
|
442
|
+
required=False, default=constants.DEFAULT_DM_BLUR_SIZE, min_val=1, max_val=21)
|
|
443
|
+
self.add_field_to_layout(
|
|
444
|
+
q_depthmap.layout(), 'depthmap_smooth_size', FIELD_INT, 'Smooth size (px)',
|
|
445
|
+
required=False, default=constants.DEFAULT_DM_SMOOTH_SIZE, min_val=0, max_val=256)
|
|
446
|
+
self.add_field_to_layout(
|
|
447
|
+
q_depthmap.layout(), 'depthmap_temperature', FIELD_FLOAT, 'Temperature',
|
|
448
|
+
required=False, default=constants.DEFAULT_DM_TEMPERATURE,
|
|
337
449
|
min_val=0, max_val=1, step=0.05)
|
|
338
|
-
self.
|
|
339
|
-
'depthmap_levels', FIELD_INT, 'Levels', required=False,
|
|
340
|
-
add_to_layout=q_depthmap.layout(),
|
|
450
|
+
self.add_field_to_layout(
|
|
451
|
+
q_depthmap.layout(), 'depthmap_levels', FIELD_INT, 'Levels', required=False,
|
|
341
452
|
default=constants.DEFAULT_DM_LEVELS, min_val=2, max_val=6)
|
|
342
|
-
self.
|
|
343
|
-
'depthmap_float_type', FIELD_COMBO,
|
|
344
|
-
|
|
345
|
-
values=constants.VALID_FLOATS,
|
|
453
|
+
self.add_field_to_layout(
|
|
454
|
+
q_depthmap.layout(), 'depthmap_float_type', FIELD_COMBO,
|
|
455
|
+
'Precision', required=False,
|
|
456
|
+
options=self.FLOAT_OPTIONS, values=constants.VALID_FLOATS,
|
|
346
457
|
default=dict(zip(constants.VALID_FLOATS,
|
|
347
458
|
self.FLOAT_OPTIONS))[constants.DEFAULT_DM_FLOAT])
|
|
348
|
-
|
|
459
|
+
layout.addRow(stacked)
|
|
349
460
|
combo.currentIndexChanged.connect(change)
|
|
350
461
|
|
|
351
462
|
|
|
@@ -353,32 +464,32 @@ class FocusStackConfigurator(FocusStackBaseConfigurator):
|
|
|
353
464
|
def create_form(self, layout, action):
|
|
354
465
|
super().create_form(layout, action)
|
|
355
466
|
if self.expert:
|
|
356
|
-
self.
|
|
357
|
-
'exif_path', FIELD_REL_PATH,
|
|
467
|
+
self.add_field_to_layout(
|
|
468
|
+
self.general_tab_layout, 'exif_path', FIELD_REL_PATH,
|
|
469
|
+
'Exif data path', required=False,
|
|
358
470
|
placeholder='relative to working path')
|
|
359
|
-
self.
|
|
360
|
-
|
|
471
|
+
self.add_field_to_layout(
|
|
472
|
+
self.general_tab_layout, 'prefix', FIELD_TEXT,
|
|
473
|
+
'Output filename prefix', required=False,
|
|
361
474
|
default=constants.DEFAULT_STACK_PREFIX,
|
|
362
475
|
placeholder=constants.DEFAULT_STACK_PREFIX)
|
|
363
|
-
self.
|
|
364
|
-
'plot_stack', FIELD_BOOL, 'Plot stack', required=False,
|
|
476
|
+
self.add_field_to_layout(
|
|
477
|
+
self.general_tab_layout, 'plot_stack', FIELD_BOOL, 'Plot stack', required=False,
|
|
365
478
|
default=constants.DEFAULT_PLOT_STACK)
|
|
366
|
-
super().common_fields(layout)
|
|
367
479
|
|
|
368
480
|
|
|
369
481
|
class FocusStackBunchConfigurator(FocusStackBaseConfigurator):
|
|
370
482
|
def create_form(self, layout, action):
|
|
371
483
|
super().create_form(layout, action)
|
|
372
|
-
self.
|
|
373
|
-
'frames', FIELD_INT, 'Frames', required=False,
|
|
484
|
+
self.add_field_to_layout(
|
|
485
|
+
self.general_tab_layout, 'frames', FIELD_INT, 'Frames', required=False,
|
|
374
486
|
default=constants.DEFAULT_FRAMES, min_val=1, max_val=100)
|
|
375
|
-
self.
|
|
376
|
-
'overlap', FIELD_INT, 'Overlapping frames', required=False,
|
|
487
|
+
self.add_field_to_layout(
|
|
488
|
+
self.general_tab_layout, 'overlap', FIELD_INT, 'Overlapping frames', required=False,
|
|
377
489
|
default=constants.DEFAULT_OVERLAP, min_val=0, max_val=100)
|
|
378
|
-
self.
|
|
379
|
-
'plot_stack', FIELD_BOOL, 'Plot stack', required=False,
|
|
490
|
+
self.add_field_to_layout(
|
|
491
|
+
self.general_tab_layout, 'plot_stack', FIELD_BOOL, 'Plot stack', required=False,
|
|
380
492
|
default=constants.DEFAULT_PLOT_STACK_BUNCH)
|
|
381
|
-
super().common_fields(layout)
|
|
382
493
|
|
|
383
494
|
|
|
384
495
|
class MultiLayerConfigurator(DefaultActionConfigurator):
|
|
@@ -435,6 +546,13 @@ class CombinedActionsConfigurator(DefaultActionConfigurator):
|
|
|
435
546
|
self.add_field(
|
|
436
547
|
'step_process', FIELD_BOOL, 'Step process', required=False,
|
|
437
548
|
default=True)
|
|
549
|
+
self.add_field(
|
|
550
|
+
'max_threads', FIELD_INT, 'Max num. of cores',
|
|
551
|
+
required=False, default=constants.DEFAULT_MAX_FWK_THREADS,
|
|
552
|
+
min_val=1, max_val=64)
|
|
553
|
+
self.add_field(
|
|
554
|
+
'chunk_submit', FIELD_BOOL, 'Submit in chunks',
|
|
555
|
+
required=False, default=constants.DEFAULT_MAX_FWK_CHUNK_SUBMIT)
|
|
438
556
|
|
|
439
557
|
|
|
440
558
|
class MaskNoiseConfigurator(DefaultActionConfigurator):
|
|
@@ -460,18 +578,21 @@ class SubsampleActionConfigurator(DefaultActionConfigurator):
|
|
|
460
578
|
self.subsample_field = None
|
|
461
579
|
self.fast_subsampling_field = None
|
|
462
580
|
|
|
463
|
-
def add_subsample_fields(self):
|
|
581
|
+
def add_subsample_fields(self, add_to_layout=None):
|
|
582
|
+
if add_to_layout is None:
|
|
583
|
+
add_to_layout = self.builder.main_layout
|
|
464
584
|
self.subsample_field = self.add_field(
|
|
465
585
|
'subsample', FIELD_COMBO, 'Subsample', required=False,
|
|
466
586
|
options=constants.FIELD_SUBSAMPLE_OPTIONS,
|
|
467
587
|
values=constants.FIELD_SUBSAMPLE_VALUES,
|
|
468
|
-
default=constants.FIELD_SUBSAMPLE_DEFAULT
|
|
588
|
+
default=constants.FIELD_SUBSAMPLE_DEFAULT,
|
|
589
|
+
add_to_layout=add_to_layout)
|
|
469
590
|
self.fast_subsampling_field = self.add_field(
|
|
470
591
|
'fast_subsampling', FIELD_BOOL, 'Fast subsampling', required=False,
|
|
471
|
-
default=constants.DEFAULT_ALIGN_FAST_SUBSAMPLING
|
|
592
|
+
default=constants.DEFAULT_ALIGN_FAST_SUBSAMPLING,
|
|
593
|
+
add_to_layout=add_to_layout)
|
|
472
594
|
|
|
473
595
|
self.subsample_field.currentTextChanged.connect(self.change_subsample)
|
|
474
|
-
|
|
475
596
|
self.change_subsample()
|
|
476
597
|
|
|
477
598
|
def change_subsample(self):
|
|
@@ -484,6 +605,7 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
|
|
|
484
605
|
TRANSFORM_OPTIONS = ['Rigid', 'Homography']
|
|
485
606
|
METHOD_OPTIONS = ['Random Sample Consensus (RANSAC)', 'Least Median (LMEDS)']
|
|
486
607
|
MATCHING_METHOD_OPTIONS = ['K-nearest neighbors', 'Hamming distance']
|
|
608
|
+
MODE_OPTIONS = ['Auto', 'Sequential', 'Parallel']
|
|
487
609
|
|
|
488
610
|
def __init__(self, expert, current_wd):
|
|
489
611
|
super().__init__(expert, current_wd)
|
|
@@ -492,13 +614,14 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
|
|
|
492
614
|
self.detector_field = None
|
|
493
615
|
self.descriptor_field = None
|
|
494
616
|
self.matching_method_field = None
|
|
617
|
+
self.tab_widget = None
|
|
618
|
+
self.current_tab_layout = None
|
|
495
619
|
|
|
496
620
|
def show_info(self, message, timeout=3000):
|
|
497
621
|
self.info_label.setText(message)
|
|
498
|
-
self.info_label.setVisible(True)
|
|
499
622
|
timer = QTimer(self.info_label)
|
|
500
623
|
timer.setSingleShot(True)
|
|
501
|
-
timer.timeout.connect(self.info_label.
|
|
624
|
+
timer.timeout.connect(lambda: self.info_label.setText(''))
|
|
502
625
|
timer.start(timeout)
|
|
503
626
|
|
|
504
627
|
def change_match_config(self):
|
|
@@ -533,131 +656,178 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
|
|
|
533
656
|
self.detector_field = None
|
|
534
657
|
self.descriptor_field = None
|
|
535
658
|
self.matching_method_field = None
|
|
659
|
+
self.tab_widget = self.create_tab_widget(layout)
|
|
536
660
|
if self.expert:
|
|
537
|
-
self.
|
|
538
|
-
self.
|
|
539
|
-
self.
|
|
540
|
-
self.
|
|
541
|
-
|
|
542
|
-
self.
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
self.
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
661
|
+
feature_layout = self.add_tab(self.tab_widget, "Feature extraction")
|
|
662
|
+
self.create_feature_tab(feature_layout)
|
|
663
|
+
transform_layout = self.add_tab(self.tab_widget, "Transform")
|
|
664
|
+
self.create_transform_tab(transform_layout)
|
|
665
|
+
border_layout = self.add_tab(self.tab_widget, "Border")
|
|
666
|
+
self.create_border_tab(border_layout)
|
|
667
|
+
misc_layout = self.add_tab(self.tab_widget, "Miscellanea")
|
|
668
|
+
self.create_miscellanea_tab(misc_layout)
|
|
669
|
+
|
|
670
|
+
def create_feature_tab(self, layout):
|
|
671
|
+
self.add_bold_label_to_layout(layout, "Feature identification:")
|
|
672
|
+
self.detector_field = self.add_field_to_layout(
|
|
673
|
+
layout, 'detector', FIELD_COMBO, 'Detector', required=False,
|
|
674
|
+
options=constants.VALID_DETECTORS, default=constants.DEFAULT_DETECTOR)
|
|
675
|
+
self.descriptor_field = self.add_field_to_layout(
|
|
676
|
+
layout, 'descriptor', FIELD_COMBO, 'Descriptor', required=False,
|
|
677
|
+
options=constants.VALID_DESCRIPTORS, default=constants.DEFAULT_DESCRIPTOR)
|
|
678
|
+
self.detector_field.setToolTip(
|
|
679
|
+
"SIFT: Requires SIFT descriptor and K-NN matching\n"
|
|
680
|
+
"ORB/AKAZE: Work best with Hamming distance"
|
|
681
|
+
)
|
|
682
|
+
self.descriptor_field.setToolTip(
|
|
683
|
+
"SIFT: Requires K-NN matching\n"
|
|
684
|
+
"ORB/AKAZE: Require Hamming distance with ORB/AKAZE detectors"
|
|
685
|
+
)
|
|
686
|
+
self.detector_field.currentIndexChanged.connect(self.change_match_config)
|
|
687
|
+
self.descriptor_field.currentIndexChanged.connect(self.change_match_config)
|
|
688
|
+
self.info_label = QLabel()
|
|
689
|
+
self.info_label.setStyleSheet("color: orange; font-style: italic;")
|
|
690
|
+
layout.addRow(self.info_label)
|
|
691
|
+
self.add_bold_label_to_layout(layout, "Feature matching:")
|
|
692
|
+
self.matching_method_field = self.add_field_to_layout(
|
|
693
|
+
layout, 'match_method', FIELD_COMBO, 'Match method', required=False,
|
|
694
|
+
options=self.MATCHING_METHOD_OPTIONS, values=constants.VALID_MATCHING_METHODS,
|
|
695
|
+
default=constants.DEFAULT_MATCHING_METHOD)
|
|
696
|
+
self.matching_method_field.setToolTip(
|
|
697
|
+
"Automatically selected based on detector/descriptor combination"
|
|
698
|
+
)
|
|
699
|
+
self.matching_method_field.currentIndexChanged.connect(self.change_match_config)
|
|
700
|
+
self.add_field_to_layout(
|
|
701
|
+
layout, 'flann_idx_kdtree', FIELD_INT, 'Flann idx kdtree', required=False,
|
|
702
|
+
default=constants.DEFAULT_FLANN_IDX_KDTREE,
|
|
703
|
+
min_val=0, max_val=10)
|
|
704
|
+
self.add_field_to_layout(
|
|
705
|
+
layout, 'flann_trees', FIELD_INT, 'Flann trees', required=False,
|
|
706
|
+
default=constants.DEFAULT_FLANN_TREES,
|
|
707
|
+
min_val=0, max_val=10)
|
|
708
|
+
self.add_field_to_layout(
|
|
709
|
+
layout, 'flann_checks', FIELD_INT, 'Flann checks', required=False,
|
|
710
|
+
default=constants.DEFAULT_FLANN_CHECKS,
|
|
711
|
+
min_val=0, max_val=1000)
|
|
712
|
+
self.add_field_to_layout(
|
|
713
|
+
layout, 'threshold', FIELD_FLOAT, 'Threshold', required=False,
|
|
714
|
+
default=constants.DEFAULT_ALIGN_THRESHOLD,
|
|
715
|
+
min_val=0, max_val=1, step=0.05)
|
|
716
|
+
self.add_subsample_fields(add_to_layout=layout)
|
|
717
|
+
|
|
718
|
+
def create_transform_tab(self, layout):
|
|
719
|
+
self.add_bold_label_to_layout(layout, "Transform:")
|
|
720
|
+
transform = self.add_field_to_layout(
|
|
721
|
+
layout, 'transform', FIELD_COMBO, 'Transform', required=False,
|
|
722
|
+
options=self.TRANSFORM_OPTIONS, values=constants.VALID_TRANSFORMS,
|
|
723
|
+
default=constants.DEFAULT_TRANSFORM)
|
|
724
|
+
method = self.add_field_to_layout(
|
|
725
|
+
layout, 'align_method', FIELD_COMBO, 'Align method', required=False,
|
|
726
|
+
options=self.METHOD_OPTIONS, values=constants.VALID_ALIGN_METHODS,
|
|
727
|
+
default=constants.DEFAULT_ALIGN_METHOD)
|
|
728
|
+
rans_threshold = self.add_field_to_layout(
|
|
729
|
+
layout, 'rans_threshold', FIELD_FLOAT, 'RANSAC threshold (px)', required=False,
|
|
730
|
+
default=constants.DEFAULT_RANS_THRESHOLD, min_val=0, max_val=20, step=0.1)
|
|
731
|
+
self.add_field_to_layout(
|
|
732
|
+
layout, 'min_good_matches', FIELD_INT, "Min. good matches", required=False,
|
|
733
|
+
default=constants.DEFAULT_ALIGN_MIN_GOOD_MATCHES, min_val=0, max_val=500)
|
|
734
|
+
|
|
735
|
+
def change_method():
|
|
736
|
+
text = method.currentText()
|
|
737
|
+
if text == self.METHOD_OPTIONS[0]:
|
|
738
|
+
rans_threshold.setEnabled(True)
|
|
739
|
+
elif text == self.METHOD_OPTIONS[1]:
|
|
740
|
+
rans_threshold.setEnabled(False)
|
|
741
|
+
|
|
742
|
+
method.currentIndexChanged.connect(change_method)
|
|
743
|
+
change_method()
|
|
744
|
+
self.add_field_to_layout(
|
|
745
|
+
layout, 'align_confidence', FIELD_FLOAT, 'Confidence (%)',
|
|
746
|
+
required=False, decimals=1,
|
|
747
|
+
default=constants.DEFAULT_ALIGN_CONFIDENCE,
|
|
748
|
+
min_val=70.0, max_val=100.0, step=0.1)
|
|
749
|
+
refine_iters = self.add_field_to_layout(
|
|
750
|
+
layout, 'refine_iters', FIELD_INT, 'Refinement iterations (Rigid)', required=False,
|
|
751
|
+
default=constants.DEFAULT_REFINE_ITERS, min_val=0, max_val=1000)
|
|
752
|
+
max_iters = self.add_field_to_layout(
|
|
753
|
+
layout, 'max_iters', FIELD_INT, 'Max. iterations (Homography)', required=False,
|
|
754
|
+
default=constants.DEFAULT_ALIGN_MAX_ITERS, min_val=0, max_val=5000)
|
|
755
|
+
|
|
756
|
+
def change_transform():
|
|
757
|
+
text = transform.currentText()
|
|
758
|
+
if text == self.TRANSFORM_OPTIONS[0]:
|
|
759
|
+
refine_iters.setEnabled(True)
|
|
760
|
+
max_iters.setEnabled(False)
|
|
761
|
+
elif text == self.TRANSFORM_OPTIONS[1]:
|
|
762
|
+
refine_iters.setEnabled(False)
|
|
763
|
+
max_iters.setEnabled(True)
|
|
764
|
+
|
|
765
|
+
transform.currentIndexChanged.connect(change_transform)
|
|
766
|
+
change_transform()
|
|
767
|
+
self.add_field_to_layout(
|
|
768
|
+
layout, 'abort_abnormal', FIELD_BOOL, 'Abort on abnormal transf.',
|
|
769
|
+
required=False, default=constants.DEFAULT_ALIGN_ABORT_ABNORMAL)
|
|
770
|
+
|
|
771
|
+
def create_border_tab(self, layout):
|
|
772
|
+
self.add_bold_label_to_layout(layout, "Border:")
|
|
773
|
+
self.add_field_to_layout(
|
|
774
|
+
layout, 'border_mode', FIELD_COMBO, 'Border mode', required=False,
|
|
775
|
+
options=self.BORDER_MODE_OPTIONS,
|
|
776
|
+
values=constants.VALID_BORDER_MODES,
|
|
777
|
+
default=constants.DEFAULT_BORDER_MODE)
|
|
778
|
+
self.add_field_to_layout(
|
|
779
|
+
layout, 'border_value', FIELD_INT_TUPLE,
|
|
780
|
+
'Border value (if constant)', required=False, size=4,
|
|
781
|
+
default=constants.DEFAULT_BORDER_VALUE,
|
|
782
|
+
labels=constants.RGBA_LABELS,
|
|
783
|
+
min_val=constants.DEFAULT_BORDER_VALUE, max_val=[255] * 4)
|
|
784
|
+
self.add_field_to_layout(
|
|
785
|
+
layout, 'border_blur', FIELD_FLOAT, 'Border blur', required=False,
|
|
786
|
+
default=constants.DEFAULT_BORDER_BLUR,
|
|
787
|
+
min_val=0, max_val=1000, step=1)
|
|
788
|
+
|
|
789
|
+
def create_miscellanea_tab(self, layout):
|
|
790
|
+
self.add_bold_label_to_layout(layout, "Miscellanea:")
|
|
791
|
+
if self.expert:
|
|
792
|
+
mode = self.add_field_to_layout(
|
|
793
|
+
layout, 'mode', FIELD_COMBO, 'Mode',
|
|
794
|
+
required=False, options=self.MODE_OPTIONS, values=constants.ALIGN_VALID_MODES,
|
|
795
|
+
default=dict(zip(constants.ALIGN_VALID_MODES,
|
|
796
|
+
self.MODE_OPTIONS))[constants.DEFAULT_ALIGN_MODE])
|
|
797
|
+
memory_limit = self.add_field_to_layout(
|
|
798
|
+
layout, 'memory_limit', FIELD_FLOAT, 'Memory limit (approx., GBytes)',
|
|
799
|
+
required=False, default=constants.DEFAULT_ALIGN_MEMORY_LIMIT_GB,
|
|
800
|
+
min_val=1.0, max_val=64.0)
|
|
801
|
+
max_threads = self.add_field_to_layout(
|
|
802
|
+
layout, 'max_threads', FIELD_INT, 'Max num. of cores',
|
|
803
|
+
required=False, default=constants.DEFAULT_ALIGN_MAX_THREADS,
|
|
804
|
+
min_val=1, max_val=64)
|
|
805
|
+
chunk_submit = self.add_field_to_layout(
|
|
806
|
+
layout, 'chunk_submit', FIELD_BOOL, 'Submit in chunks',
|
|
807
|
+
required=False, default=constants.DEFAULT_ALIGN_CHUNK_SUBMIT)
|
|
808
|
+
bw_matching = self.add_field_to_layout(
|
|
809
|
+
layout, 'bw_matching', FIELD_BOOL, 'Match using black & white',
|
|
810
|
+
required=False, default=constants.DEFAULT_ALIGN_BW_MATCHING)
|
|
811
|
+
|
|
812
|
+
def change_mode():
|
|
813
|
+
text = mode.currentText()
|
|
814
|
+
enabled = text != self.MODE_OPTIONS[1]
|
|
815
|
+
memory_limit.setEnabled(enabled)
|
|
816
|
+
max_threads.setEnabled(enabled)
|
|
817
|
+
chunk_submit.setEnabled(enabled)
|
|
818
|
+
bw_matching.setEnabled(enabled)
|
|
819
|
+
|
|
820
|
+
mode.currentIndexChanged.connect(change_mode)
|
|
821
|
+
|
|
822
|
+
self.add_field_to_layout(
|
|
823
|
+
layout, 'plot_summary', FIELD_BOOL, 'Plot summary',
|
|
655
824
|
required=False, default=False)
|
|
656
|
-
|
|
657
|
-
|
|
825
|
+
|
|
826
|
+
self.add_field_to_layout(
|
|
827
|
+
layout, 'plot_matches', FIELD_BOOL, 'Plot matches',
|
|
658
828
|
required=False, default=False)
|
|
659
829
|
|
|
660
|
-
def update_params(self, params
|
|
830
|
+
def update_params(self, params):
|
|
661
831
|
if self.detector_field and self.descriptor_field and self.matching_method_field:
|
|
662
832
|
try:
|
|
663
833
|
detector = self.detector_field.currentText()
|