shinestacker 1.1.0__py3-none-any.whl → 1.2.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/__init__.py +4 -1
- shinestacker/algorithms/align.py +149 -34
- shinestacker/algorithms/balance.py +364 -166
- shinestacker/algorithms/base_stack_algo.py +6 -0
- shinestacker/algorithms/depth_map.py +1 -1
- shinestacker/algorithms/multilayer.py +22 -13
- shinestacker/algorithms/noise_detection.py +7 -8
- shinestacker/algorithms/pyramid.py +3 -2
- shinestacker/algorithms/pyramid_auto.py +141 -0
- shinestacker/algorithms/pyramid_tiles.py +199 -44
- shinestacker/algorithms/stack.py +20 -20
- shinestacker/algorithms/stack_framework.py +136 -156
- shinestacker/algorithms/utils.py +175 -1
- shinestacker/algorithms/vignetting.py +26 -8
- shinestacker/config/constants.py +31 -6
- shinestacker/core/framework.py +12 -12
- shinestacker/gui/action_config.py +59 -7
- shinestacker/gui/action_config_dialog.py +427 -283
- shinestacker/gui/base_form_dialog.py +11 -6
- shinestacker/gui/gui_images.py +10 -10
- shinestacker/gui/gui_run.py +1 -1
- shinestacker/gui/main_window.py +6 -5
- shinestacker/gui/menu_manager.py +16 -2
- shinestacker/gui/new_project.py +26 -22
- shinestacker/gui/project_controller.py +43 -27
- shinestacker/gui/project_converter.py +2 -8
- shinestacker/gui/project_editor.py +50 -27
- shinestacker/gui/tab_widget.py +3 -3
- shinestacker/retouch/exif_data.py +5 -5
- shinestacker/retouch/shortcuts_help.py +4 -4
- shinestacker/retouch/vignetting_filter.py +12 -8
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.1.dist-info}/METADATA +1 -1
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.1.dist-info}/RECORD +38 -37
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.1.dist-info}/WHEEL +0 -0
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.1.dist-info}/entry_points.txt +0 -0
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.1.dist-info}/licenses/LICENSE +0 -0
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.1.dist-info}/top_level.txt +0 -0
|
@@ -2,27 +2,38 @@
|
|
|
2
2
|
# pylint: disable=E0606, W0718, R1702, W0102, W0221
|
|
3
3
|
import traceback
|
|
4
4
|
from typing import Dict, Any
|
|
5
|
-
from PySide6.QtWidgets import (QWidget, QPushButton, QHBoxLayout, QLabel,
|
|
6
|
-
QMessageBox, QStackedWidget, QFormLayout)
|
|
5
|
+
from PySide6.QtWidgets import (QWidget, QPushButton, QHBoxLayout, QLabel, QScrollArea,
|
|
6
|
+
QMessageBox, QStackedWidget, QFormLayout, QDialog)
|
|
7
7
|
from PySide6.QtCore import Qt, QTimer
|
|
8
8
|
from .. config.constants import constants
|
|
9
9
|
from .. algorithms.align import validate_align_config
|
|
10
10
|
from .project_model import ActionConfig
|
|
11
|
-
from .base_form_dialog import
|
|
11
|
+
from .base_form_dialog import create_form_layout
|
|
12
12
|
from . action_config import (
|
|
13
13
|
FieldBuilder, ActionConfigurator,
|
|
14
14
|
FIELD_TEXT, FIELD_ABS_PATH, FIELD_REL_PATH, FIELD_FLOAT,
|
|
15
|
-
FIELD_INT, FIELD_INT_TUPLE, FIELD_BOOL, FIELD_COMBO
|
|
15
|
+
FIELD_INT, FIELD_INT_TUPLE, FIELD_BOOL, FIELD_COMBO, FIELD_REF_IDX
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
class ActionConfigDialog(
|
|
19
|
+
class ActionConfigDialog(QDialog):
|
|
20
20
|
def __init__(self, action: ActionConfig, current_wd, parent=None):
|
|
21
|
-
super().__init__(
|
|
21
|
+
super().__init__(parent)
|
|
22
|
+
self.setWindowTitle(f"Configure {action.type_name}")
|
|
23
|
+
self.form_layout = create_form_layout(self)
|
|
22
24
|
self.current_wd = current_wd
|
|
23
25
|
self.action = action
|
|
26
|
+
scroll_area = QScrollArea()
|
|
27
|
+
scroll_area.setWidgetResizable(True)
|
|
28
|
+
container_widget = QWidget()
|
|
29
|
+
self.container_layout = QFormLayout(container_widget)
|
|
30
|
+
self.container_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
|
|
31
|
+
self.container_layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
|
|
32
|
+
self.container_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)
|
|
33
|
+
self.container_layout.setLabelAlignment(Qt.AlignLeft)
|
|
24
34
|
self.configurator = self.get_configurator(action.type_name)
|
|
25
|
-
self.configurator.create_form(self.
|
|
35
|
+
self.configurator.create_form(self.container_layout, action)
|
|
36
|
+
scroll_area.setWidget(container_widget)
|
|
26
37
|
button_box = QHBoxLayout()
|
|
27
38
|
ok_button = QPushButton("OK")
|
|
28
39
|
ok_button.setFocus()
|
|
@@ -32,10 +43,47 @@ class ActionConfigDialog(BaseFormDialog):
|
|
|
32
43
|
button_box.addWidget(cancel_button)
|
|
33
44
|
button_box.addWidget(reset_button)
|
|
34
45
|
reset_button.clicked.connect(self.reset_to_defaults)
|
|
35
|
-
self.
|
|
46
|
+
self.form_layout.addRow(scroll_area)
|
|
47
|
+
self.form_layout.addRow(button_box)
|
|
48
|
+
QTimer.singleShot(0, self.adjust_dialog_size)
|
|
36
49
|
ok_button.clicked.connect(self.accept)
|
|
37
50
|
cancel_button.clicked.connect(self.reject)
|
|
38
51
|
|
|
52
|
+
def adjust_dialog_size(self):
|
|
53
|
+
screen_geometry = self.screen().availableGeometry()
|
|
54
|
+
screen_height = screen_geometry.height()
|
|
55
|
+
screen_width = screen_geometry.width()
|
|
56
|
+
scroll_area = self.findChild(QScrollArea)
|
|
57
|
+
container_widget = scroll_area.widget()
|
|
58
|
+
container_size = container_widget.sizeHint()
|
|
59
|
+
container_height = container_size.height()
|
|
60
|
+
container_width = container_size.width()
|
|
61
|
+
button_row_height = 50 # Approx height of button row
|
|
62
|
+
margins_height = 40 # Approx. height of margins
|
|
63
|
+
total_height_needed = container_height + button_row_height + margins_height
|
|
64
|
+
if total_height_needed < screen_height * 0.8:
|
|
65
|
+
width = max(container_width + 40, 600)
|
|
66
|
+
height = total_height_needed
|
|
67
|
+
self.resize(width, height)
|
|
68
|
+
scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
|
69
|
+
else:
|
|
70
|
+
max_height = int(screen_height * 0.9)
|
|
71
|
+
width = max(container_width + 40, 600)
|
|
72
|
+
width = min(width, int(screen_width * 0.9))
|
|
73
|
+
self.resize(width, max_height)
|
|
74
|
+
self.setMaximumHeight(max_height)
|
|
75
|
+
scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
76
|
+
self.setMinimumHeight(min(max_height, 500))
|
|
77
|
+
self.setMinimumWidth(width)
|
|
78
|
+
self.center_on_screen()
|
|
79
|
+
|
|
80
|
+
def center_on_screen(self):
|
|
81
|
+
screen_geometry = self.screen().availableGeometry()
|
|
82
|
+
center_point = screen_geometry.center()
|
|
83
|
+
frame_geometry = self.frameGeometry()
|
|
84
|
+
frame_geometry.moveCenter(center_point)
|
|
85
|
+
self.move(frame_geometry.topLeft())
|
|
86
|
+
|
|
39
87
|
def get_configurator(self, action_type: str) -> ActionConfigurator:
|
|
40
88
|
configurators = {
|
|
41
89
|
constants.ACTION_JOB: JobConfigurator,
|
|
@@ -53,9 +101,8 @@ class ActionConfigDialog(BaseFormDialog):
|
|
|
53
101
|
action_type, DefaultActionConfigurator)(self.expert(), self.current_wd)
|
|
54
102
|
|
|
55
103
|
def accept(self):
|
|
56
|
-
self.parent().project_editor.add_undo(self.parent().project().clone())
|
|
57
104
|
if self.configurator.update_params(self.action.params):
|
|
58
|
-
self.parent().mark_as_modified()
|
|
105
|
+
self.parent().mark_as_modified(True, "Modify Configuration")
|
|
59
106
|
super().accept()
|
|
60
107
|
else:
|
|
61
108
|
self.parent().project_editor.pop_undo()
|
|
@@ -83,81 +130,107 @@ class NoNameActionConfigurator(ActionConfigurator):
|
|
|
83
130
|
def add_bold_label(self, label):
|
|
84
131
|
label = QLabel(label)
|
|
85
132
|
label.setStyleSheet("font-weight: bold")
|
|
86
|
-
self.
|
|
133
|
+
self.add_row(label)
|
|
134
|
+
|
|
135
|
+
def add_row(self, row):
|
|
136
|
+
self.builder.main_layout.addRow(row)
|
|
137
|
+
|
|
138
|
+
def add_field(self, tag, field_type, label,
|
|
139
|
+
required=False, add_to_layout=None, **kwargs):
|
|
140
|
+
return self.builder.add_field(tag, field_type, label, required, add_to_layout, **kwargs)
|
|
87
141
|
|
|
88
142
|
|
|
89
143
|
class DefaultActionConfigurator(NoNameActionConfigurator):
|
|
90
144
|
def create_form(self, layout, action, tag='Action'):
|
|
91
145
|
self.builder = FieldBuilder(layout, action, self.current_wd)
|
|
92
|
-
self.
|
|
146
|
+
self.add_field(
|
|
147
|
+
'name', FIELD_TEXT, f'{tag} name', required=True)
|
|
93
148
|
|
|
94
149
|
|
|
95
150
|
class JobConfigurator(DefaultActionConfigurator):
|
|
96
151
|
def create_form(self, layout, action):
|
|
97
152
|
super().create_form(layout, action, "Job")
|
|
98
|
-
self.
|
|
99
|
-
|
|
100
|
-
|
|
153
|
+
self.add_field(
|
|
154
|
+
'working_path', FIELD_ABS_PATH, 'Working path', required=True)
|
|
155
|
+
self.add_field(
|
|
156
|
+
'input_path', FIELD_REL_PATH, 'Input path', required=False,
|
|
157
|
+
must_exist=True, placeholder='relative to working path')
|
|
101
158
|
|
|
102
159
|
|
|
103
160
|
class NoiseDetectionConfigurator(DefaultActionConfigurator):
|
|
104
161
|
def create_form(self, layout, action):
|
|
105
162
|
super().create_form(layout, action)
|
|
106
|
-
self.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
self.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
163
|
+
self.add_field(
|
|
164
|
+
'working_path', FIELD_ABS_PATH, 'Working path', required=True,
|
|
165
|
+
placeholder='inherit from job')
|
|
166
|
+
self.add_field(
|
|
167
|
+
'input_path', FIELD_REL_PATH,
|
|
168
|
+
f'Input path (separate by {constants.PATH_SEPARATOR})',
|
|
169
|
+
required=False, multiple_entries=True,
|
|
170
|
+
placeholder='relative to working path')
|
|
171
|
+
self.add_field(
|
|
172
|
+
'max_frames', FIELD_INT, 'Max. num. of frames (0 = All)',
|
|
173
|
+
required=False,
|
|
174
|
+
default=constants.DEFAULT_NOISE_MAX_FRAMES, min_val=0, max_val=1000)
|
|
175
|
+
self.add_field(
|
|
176
|
+
'channel_thresholds', FIELD_INT_TUPLE, 'Noise threshold',
|
|
177
|
+
required=False, size=3,
|
|
178
|
+
default=constants.DEFAULT_CHANNEL_THRESHOLDS,
|
|
179
|
+
labels=constants.RGB_LABELS, min_val=[1] * 3, max_val=[1000] * 3)
|
|
119
180
|
if self.expert:
|
|
120
|
-
self.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
181
|
+
self.add_field(
|
|
182
|
+
'blur_size', FIELD_INT, 'Blur size (px)', required=False,
|
|
183
|
+
default=constants.DEFAULT_BLUR_SIZE, min_val=1, max_val=50)
|
|
184
|
+
self.add_field(
|
|
185
|
+
'file_name', FIELD_TEXT, 'File name', required=False,
|
|
186
|
+
default=constants.DEFAULT_NOISE_MAP_FILENAME,
|
|
187
|
+
placeholder=constants.DEFAULT_NOISE_MAP_FILENAME)
|
|
125
188
|
self.add_bold_label("Miscellanea:")
|
|
126
|
-
self.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
189
|
+
self.add_field(
|
|
190
|
+
'plot_histograms', FIELD_BOOL, 'Plot histograms', required=False,
|
|
191
|
+
default=False)
|
|
192
|
+
self.add_field(
|
|
193
|
+
'plot_path', FIELD_REL_PATH, 'Plots path', required=False,
|
|
194
|
+
default=constants.DEFAULT_PLOTS_PATH,
|
|
195
|
+
placeholder='relative to working path')
|
|
196
|
+
self.add_field(
|
|
197
|
+
'plot_range', FIELD_INT_TUPLE, 'Plot range', required=False,
|
|
198
|
+
size=2, default=constants.DEFAULT_NOISE_PLOT_RANGE,
|
|
199
|
+
labels=['min', 'max'], min_val=[0] * 2, max_val=[1000] * 2)
|
|
134
200
|
|
|
135
201
|
|
|
136
202
|
class FocusStackBaseConfigurator(DefaultActionConfigurator):
|
|
137
203
|
ENERGY_OPTIONS = ['Laplacian', 'Sobel']
|
|
138
204
|
MAP_TYPE_OPTIONS = ['Average', 'Maximum']
|
|
139
205
|
FLOAT_OPTIONS = ['float 32 bits', 'float 64 bits']
|
|
206
|
+
MODE_OPTIONS = ['Auto', 'All in memory', 'Tiled I/O buffered']
|
|
140
207
|
|
|
141
208
|
def create_form(self, layout, action):
|
|
142
209
|
super().create_form(layout, action)
|
|
143
210
|
if self.expert:
|
|
144
|
-
self.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
211
|
+
self.add_field(
|
|
212
|
+
'working_path', FIELD_ABS_PATH, 'Working path', required=False)
|
|
213
|
+
self.add_field(
|
|
214
|
+
'input_path', FIELD_REL_PATH, 'Input path', required=False,
|
|
215
|
+
placeholder='relative to working path')
|
|
216
|
+
self.add_field(
|
|
217
|
+
'output_path', FIELD_REL_PATH, 'Output path', required=False,
|
|
218
|
+
placeholder='relative to working path')
|
|
219
|
+
self.add_field(
|
|
220
|
+
'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
|
|
221
|
+
required=False, default=True)
|
|
151
222
|
|
|
152
223
|
def common_fields(self, layout):
|
|
153
|
-
self.
|
|
154
|
-
|
|
224
|
+
self.add_field(
|
|
225
|
+
'denoise_amount', FIELD_FLOAT, 'Denoise', required=False,
|
|
226
|
+
default=0, min_val=0, max_val=10)
|
|
155
227
|
self.add_bold_label("Stacking algorithm:")
|
|
156
|
-
combo = self.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
228
|
+
combo = self.add_field(
|
|
229
|
+
'stacker', FIELD_COMBO, 'Stacking algorithm', required=True,
|
|
230
|
+
options=constants.STACK_ALGO_OPTIONS,
|
|
231
|
+
default=constants.STACK_ALGO_DEFAULT)
|
|
232
|
+
q_pyramid, q_depthmap = QWidget(), QWidget()
|
|
233
|
+
for q in [q_pyramid, q_depthmap]:
|
|
161
234
|
layout = QFormLayout()
|
|
162
235
|
layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
|
|
163
236
|
layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
|
|
@@ -166,87 +239,113 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
|
|
|
166
239
|
q.setLayout(layout)
|
|
167
240
|
stacked = QStackedWidget()
|
|
168
241
|
stacked.addWidget(q_pyramid)
|
|
169
|
-
stacked.addWidget(q_pyramid_tiles)
|
|
170
242
|
stacked.addWidget(q_depthmap)
|
|
171
243
|
|
|
172
244
|
def change():
|
|
173
245
|
text = combo.currentText()
|
|
174
246
|
if text == constants.STACK_ALGO_PYRAMID:
|
|
175
247
|
stacked.setCurrentWidget(q_pyramid)
|
|
176
|
-
if text == constants.STACK_ALGO_PYRAMID_TILES:
|
|
177
|
-
stacked.setCurrentWidget(q_pyramid_tiles)
|
|
178
248
|
elif text == constants.STACK_ALGO_DEPTH_MAP:
|
|
179
249
|
stacked.setCurrentWidget(q_depthmap)
|
|
180
250
|
change()
|
|
181
251
|
if self.expert:
|
|
182
|
-
self.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
self.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
self.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
252
|
+
self.add_field(
|
|
253
|
+
'pyramid_min_size', FIELD_INT, 'Minimum size (px)',
|
|
254
|
+
required=False, add_to_layout=q_pyramid.layout(),
|
|
255
|
+
default=constants.DEFAULT_PY_MIN_SIZE, min_val=2, max_val=256)
|
|
256
|
+
self.add_field(
|
|
257
|
+
'pyramid_kernel_size', FIELD_INT, 'Kernel size (px)',
|
|
258
|
+
required=False, add_to_layout=q_pyramid.layout(),
|
|
259
|
+
default=constants.DEFAULT_PY_KERNEL_SIZE, min_val=3, max_val=21)
|
|
260
|
+
self.add_field(
|
|
261
|
+
'pyramid_gen_kernel', FIELD_FLOAT, 'Gen. kernel',
|
|
262
|
+
required=False, add_to_layout=q_pyramid.layout(),
|
|
263
|
+
default=constants.DEFAULT_PY_GEN_KERNEL,
|
|
264
|
+
min_val=0.0, max_val=2.0)
|
|
265
|
+
self.add_field(
|
|
266
|
+
'pyramid_float_type', FIELD_COMBO, 'Precision', required=False,
|
|
267
|
+
add_to_layout=q_pyramid.layout(),
|
|
268
|
+
options=self.FLOAT_OPTIONS, values=constants.VALID_FLOATS,
|
|
269
|
+
default=dict(zip(constants.VALID_FLOATS,
|
|
270
|
+
self.FLOAT_OPTIONS))[constants.DEFAULT_PY_FLOAT])
|
|
271
|
+
mode = self.add_field(
|
|
272
|
+
'pyramid_mode', FIELD_COMBO, 'Mode',
|
|
273
|
+
required=False, add_to_layout=q_pyramid.layout(),
|
|
274
|
+
options=self.MODE_OPTIONS, values=constants.PY_VALID_MODES,
|
|
275
|
+
default=dict(zip(constants.PY_VALID_MODES,
|
|
276
|
+
self.MODE_OPTIONS))[constants.DEFAULT_PY_MODE])
|
|
277
|
+
memory_limit = self.add_field(
|
|
278
|
+
'pyramid_memory_limit', FIELD_FLOAT, 'Memory limit (approx., GBytes)',
|
|
279
|
+
required=False, add_to_layout=q_pyramid.layout(),
|
|
280
|
+
default=constants.DEFAULT_PY_MEMORY_LIMIT_GB,
|
|
281
|
+
min_val=1.0, max_val=64.0)
|
|
282
|
+
max_threads = self.add_field(
|
|
283
|
+
'pyramid_max_threads', FIELD_INT, 'Max num. of cores',
|
|
284
|
+
required=False, add_to_layout=q_pyramid.layout(),
|
|
285
|
+
default=constants.DEFAULT_PY_MAX_THREADS,
|
|
286
|
+
min_val=1, max_val=64)
|
|
287
|
+
tile_size = self.add_field(
|
|
288
|
+
'pyramid_tile_size', FIELD_INT, 'Tile size (px)',
|
|
289
|
+
required=False, add_to_layout=q_pyramid.layout(),
|
|
290
|
+
default=constants.DEFAULT_PY_TILE_SIZE,
|
|
291
|
+
min_val=128, max_val=2048)
|
|
292
|
+
n_tiled_layers = self.add_field(
|
|
293
|
+
'pyramid_n_tiled_layers', FIELD_INT, 'Num. tiled layers',
|
|
294
|
+
required=False, add_to_layout=q_pyramid.layout(),
|
|
295
|
+
default=constants.DEFAULT_PY_N_TILED_LAYERS,
|
|
296
|
+
min_val=0, max_val=6)
|
|
297
|
+
|
|
298
|
+
def change_mode():
|
|
299
|
+
text = mode.currentText()
|
|
300
|
+
enabled = text == self.MODE_OPTIONS[2]
|
|
301
|
+
tile_size.setEnabled(enabled)
|
|
302
|
+
n_tiled_layers.setEnabled(enabled)
|
|
303
|
+
memory_limit.setEnabled(text == self.MODE_OPTIONS[0])
|
|
304
|
+
max_threads.setEnabled(text != self.MODE_OPTIONS[1])
|
|
305
|
+
|
|
306
|
+
mode.currentIndexChanged.connect(change_mode)
|
|
307
|
+
change_mode()
|
|
308
|
+
self.add_field(
|
|
309
|
+
'depthmap_energy', FIELD_COMBO, 'Energy', required=False,
|
|
310
|
+
add_to_layout=q_depthmap.layout(),
|
|
311
|
+
options=self.ENERGY_OPTIONS, values=constants.VALID_DM_ENERGY,
|
|
312
|
+
default=dict(zip(constants.VALID_DM_ENERGY,
|
|
313
|
+
self.ENERGY_OPTIONS))[constants.DEFAULT_DM_ENERGY])
|
|
314
|
+
self.add_field(
|
|
315
|
+
'map_type', FIELD_COMBO, 'Map type', required=False,
|
|
316
|
+
add_to_layout=q_depthmap.layout(),
|
|
317
|
+
options=self.MAP_TYPE_OPTIONS, values=constants.VALID_DM_MAP,
|
|
318
|
+
default=dict(zip(constants.VALID_DM_MAP,
|
|
319
|
+
self.MAP_TYPE_OPTIONS))[constants.DEFAULT_DM_MAP])
|
|
226
320
|
if self.expert:
|
|
227
|
-
self.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
self.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
321
|
+
self.add_field(
|
|
322
|
+
'depthmap_kernel_size', FIELD_INT, 'Kernel size (px)',
|
|
323
|
+
required=False, add_to_layout=q_depthmap.layout(),
|
|
324
|
+
default=constants.DEFAULT_DM_KERNEL_SIZE, min_val=3, max_val=21)
|
|
325
|
+
self.add_field(
|
|
326
|
+
'depthmap_blur_size', FIELD_INT, 'Blurl size (px)',
|
|
327
|
+
required=False, add_to_layout=q_depthmap.layout(),
|
|
328
|
+
default=constants.DEFAULT_DM_BLUR_SIZE, min_val=1, max_val=21)
|
|
329
|
+
self.add_field(
|
|
330
|
+
'depthmap_smooth_size', FIELD_INT, 'Smooth size (px)',
|
|
331
|
+
required=False, add_to_layout=q_depthmap.layout(),
|
|
332
|
+
default=constants.DEFAULT_DM_SMOOTH_SIZE, min_val=0, max_val=256)
|
|
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,
|
|
337
|
+
min_val=0, max_val=1, step=0.05)
|
|
338
|
+
self.add_field(
|
|
339
|
+
'depthmap_levels', FIELD_INT, 'Levels', required=False,
|
|
340
|
+
add_to_layout=q_depthmap.layout(),
|
|
341
|
+
default=constants.DEFAULT_DM_LEVELS, min_val=2, max_val=6)
|
|
342
|
+
self.add_field(
|
|
343
|
+
'depthmap_float_type', FIELD_COMBO, 'Precision', required=False,
|
|
344
|
+
add_to_layout=q_depthmap.layout(), options=self.FLOAT_OPTIONS,
|
|
345
|
+
values=constants.VALID_FLOATS,
|
|
346
|
+
default=dict(zip(constants.VALID_FLOATS,
|
|
347
|
+
self.FLOAT_OPTIONS))[constants.DEFAULT_DM_FLOAT])
|
|
348
|
+
self.add_row(stacked)
|
|
250
349
|
combo.currentIndexChanged.connect(change)
|
|
251
350
|
|
|
252
351
|
|
|
@@ -254,25 +353,31 @@ class FocusStackConfigurator(FocusStackBaseConfigurator):
|
|
|
254
353
|
def create_form(self, layout, action):
|
|
255
354
|
super().create_form(layout, action)
|
|
256
355
|
if self.expert:
|
|
257
|
-
self.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
356
|
+
self.add_field(
|
|
357
|
+
'exif_path', FIELD_REL_PATH, 'Exif data path', required=False,
|
|
358
|
+
placeholder='relative to working path')
|
|
359
|
+
self.add_field(
|
|
360
|
+
'prefix', FIELD_TEXT, 'Ouptut filename prefix', required=False,
|
|
361
|
+
default=constants.DEFAULT_STACK_PREFIX,
|
|
362
|
+
placeholder=constants.DEFAULT_STACK_PREFIX)
|
|
363
|
+
self.add_field(
|
|
364
|
+
'plot_stack', FIELD_BOOL, 'Plot stack', required=False,
|
|
365
|
+
default=constants.DEFAULT_PLOT_STACK)
|
|
264
366
|
super().common_fields(layout)
|
|
265
367
|
|
|
266
368
|
|
|
267
369
|
class FocusStackBunchConfigurator(FocusStackBaseConfigurator):
|
|
268
370
|
def create_form(self, layout, action):
|
|
269
371
|
super().create_form(layout, action)
|
|
270
|
-
self.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
372
|
+
self.add_field(
|
|
373
|
+
'frames', FIELD_INT, 'Frames', required=False,
|
|
374
|
+
default=constants.DEFAULT_FRAMES, min_val=1, max_val=100)
|
|
375
|
+
self.add_field(
|
|
376
|
+
'overlap', FIELD_INT, 'Overlapping frames', required=False,
|
|
377
|
+
default=constants.DEFAULT_OVERLAP, min_val=0, max_val=100)
|
|
378
|
+
self.add_field(
|
|
379
|
+
'plot_stack', FIELD_BOOL, 'Plot stack', required=False,
|
|
380
|
+
default=constants.DEFAULT_PLOT_STACK_BUNCH)
|
|
276
381
|
super().common_fields(layout)
|
|
277
382
|
|
|
278
383
|
|
|
@@ -280,83 +385,101 @@ class MultiLayerConfigurator(DefaultActionConfigurator):
|
|
|
280
385
|
def create_form(self, layout, action):
|
|
281
386
|
super().create_form(layout, action)
|
|
282
387
|
if self.expert:
|
|
283
|
-
self.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
388
|
+
self.add_field(
|
|
389
|
+
'working_path', FIELD_ABS_PATH, 'Working path', required=False)
|
|
390
|
+
self.add_field(
|
|
391
|
+
'input_path', FIELD_REL_PATH,
|
|
392
|
+
f'Input path (separate by {constants.PATH_SEPARATOR})',
|
|
393
|
+
required=False, multiple_entries=True,
|
|
394
|
+
placeholder='relative to working path')
|
|
288
395
|
if self.expert:
|
|
289
|
-
self.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
self.
|
|
296
|
-
|
|
297
|
-
|
|
396
|
+
self.add_field(
|
|
397
|
+
'output_path', FIELD_REL_PATH, 'Output path', required=False,
|
|
398
|
+
placeholder='relative to working path')
|
|
399
|
+
self.add_field(
|
|
400
|
+
'exif_path', FIELD_REL_PATH, 'Exif data path', required=False,
|
|
401
|
+
placeholder='relative to working path')
|
|
402
|
+
self.add_field(
|
|
403
|
+
'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
|
|
404
|
+
required=False, default=True)
|
|
405
|
+
self.add_field(
|
|
406
|
+
'reverse_order', FIELD_BOOL, 'Reverse file order', required=False,
|
|
407
|
+
default=constants.DEFAULT_MULTILAYER_FILE_REVERSE_ORDER)
|
|
298
408
|
|
|
299
409
|
|
|
300
410
|
class CombinedActionsConfigurator(DefaultActionConfigurator):
|
|
301
411
|
def create_form(self, layout, action):
|
|
302
412
|
super().create_form(layout, action)
|
|
303
413
|
if self.expert:
|
|
304
|
-
self.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
414
|
+
self.add_field(
|
|
415
|
+
'working_path', FIELD_ABS_PATH, 'Working path', required=False)
|
|
416
|
+
self.add_field(
|
|
417
|
+
'input_path', FIELD_REL_PATH, 'Input path', required=False,
|
|
418
|
+
must_exist=True, placeholder='relative to working path')
|
|
419
|
+
self.add_field(
|
|
420
|
+
'output_path', FIELD_REL_PATH, 'Output path', required=False,
|
|
421
|
+
placeholder='relative to working path')
|
|
422
|
+
self.add_field(
|
|
423
|
+
'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
|
|
424
|
+
required=False, default=True)
|
|
311
425
|
if self.expert:
|
|
312
|
-
self.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
self.
|
|
319
|
-
|
|
426
|
+
self.add_field(
|
|
427
|
+
'plot_path', FIELD_REL_PATH, 'Plots path', required=False,
|
|
428
|
+
default="plots", placeholder='relative to working path')
|
|
429
|
+
self.add_field(
|
|
430
|
+
'resample', FIELD_INT, 'Resample frame stack', required=False,
|
|
431
|
+
default=1, min_val=1, max_val=100)
|
|
432
|
+
self.add_field(
|
|
433
|
+
'reference_index', FIELD_REF_IDX, 'Reference frame', required=False,
|
|
434
|
+
default=0)
|
|
435
|
+
self.add_field(
|
|
436
|
+
'step_process', FIELD_BOOL, 'Step process', required=False,
|
|
437
|
+
default=True)
|
|
320
438
|
|
|
321
439
|
|
|
322
440
|
class MaskNoiseConfigurator(DefaultActionConfigurator):
|
|
323
441
|
def create_form(self, layout, action):
|
|
324
442
|
super().create_form(layout, action)
|
|
325
|
-
self.
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
443
|
+
self.add_field(
|
|
444
|
+
'noise_mask', FIELD_REL_PATH, 'Noise mask file', required=False,
|
|
445
|
+
path_type='file', must_exist=True,
|
|
446
|
+
default=constants.DEFAULT_NOISE_MAP_FILENAME,
|
|
447
|
+
placeholder=constants.DEFAULT_NOISE_MAP_FILENAME)
|
|
329
448
|
if self.expert:
|
|
330
|
-
self.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
449
|
+
self.add_field(
|
|
450
|
+
'kernel_size', FIELD_INT, 'Kernel size', required=False,
|
|
451
|
+
default=constants.DEFAULT_MN_KERNEL_SIZE, min_val=1, max_val=10)
|
|
452
|
+
self.add_field(
|
|
453
|
+
'method', FIELD_COMBO, 'Interpolation method', required=False,
|
|
454
|
+
options=['Mean', 'Median'], default='Mean')
|
|
334
455
|
|
|
335
456
|
|
|
336
|
-
class
|
|
337
|
-
def
|
|
338
|
-
super().
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
self.
|
|
353
|
-
self.builder.add_field('plot_correction', FIELD_BOOL, 'Plot correction', required=False,
|
|
354
|
-
default=False)
|
|
355
|
-
self.builder.add_field('plot_summary', FIELD_BOOL, 'Plot summary', required=False,
|
|
356
|
-
default=False)
|
|
457
|
+
class SubsampleActionConfigurator(DefaultActionConfigurator):
|
|
458
|
+
def __init__(self, expert, current_wd):
|
|
459
|
+
super().__init__(expert, current_wd)
|
|
460
|
+
self.subsample_field = None
|
|
461
|
+
self.fast_subsampling_field = None
|
|
462
|
+
|
|
463
|
+
def add_subsample_fields(self):
|
|
464
|
+
self.subsample_field = self.add_field(
|
|
465
|
+
'subsample', FIELD_COMBO, 'Subsample', required=False,
|
|
466
|
+
options=constants.FIELD_SUBSAMPLE_OPTIONS,
|
|
467
|
+
values=constants.FIELD_SUBSAMPLE_VALUES,
|
|
468
|
+
default=constants.FIELD_SUBSAMPLE_DEFAULT)
|
|
469
|
+
self.fast_subsampling_field = self.add_field(
|
|
470
|
+
'fast_subsampling', FIELD_BOOL, 'Fast subsampling', required=False,
|
|
471
|
+
default=constants.DEFAULT_ALIGN_FAST_SUBSAMPLING)
|
|
472
|
+
|
|
473
|
+
self.subsample_field.currentTextChanged.connect(self.change_subsample)
|
|
357
474
|
|
|
475
|
+
self.change_subsample()
|
|
358
476
|
|
|
359
|
-
|
|
477
|
+
def change_subsample(self):
|
|
478
|
+
self.fast_subsampling_field.setEnabled(
|
|
479
|
+
self.subsample_field.currentText() not in constants.FIELD_SUBSAMPLE_OPTIONS[:2])
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
class AlignFramesConfigurator(SubsampleActionConfigurator):
|
|
360
483
|
BORDER_MODE_OPTIONS = ['Constant', 'Replicate', 'Replicate and blur']
|
|
361
484
|
TRANSFORM_OPTIONS = ['Rigid', 'Homography']
|
|
362
485
|
METHOD_OPTIONS = ['Random Sample Consensus (RANSAC)', 'Least Median (LMEDS)']
|
|
@@ -412,21 +535,18 @@ class AlignFramesConfigurator(DefaultActionConfigurator):
|
|
|
412
535
|
self.matching_method_field = None
|
|
413
536
|
if self.expert:
|
|
414
537
|
self.add_bold_label("Feature identification:")
|
|
415
|
-
|
|
416
538
|
self.info_label = QLabel()
|
|
417
539
|
self.info_label.setStyleSheet("color: orange; font-style: italic;")
|
|
418
540
|
self.info_label.setVisible(False)
|
|
419
541
|
layout.addRow(self.info_label)
|
|
420
|
-
|
|
421
|
-
self.detector_field = self.builder.add_field(
|
|
542
|
+
self.detector_field = self.add_field(
|
|
422
543
|
'detector', FIELD_COMBO, 'Detector', required=False,
|
|
423
544
|
options=constants.VALID_DETECTORS, default=constants.DEFAULT_DETECTOR)
|
|
424
|
-
self.descriptor_field = self.
|
|
545
|
+
self.descriptor_field = self.add_field(
|
|
425
546
|
'descriptor', FIELD_COMBO, 'Descriptor', required=False,
|
|
426
547
|
options=constants.VALID_DESCRIPTORS, default=constants.DEFAULT_DESCRIPTOR)
|
|
427
|
-
|
|
428
548
|
self.add_bold_label("Feature matching:")
|
|
429
|
-
self.matching_method_field = self.
|
|
549
|
+
self.matching_method_field = self.add_field(
|
|
430
550
|
'match_method', FIELD_COMBO, 'Match method', required=False,
|
|
431
551
|
options=self.MATCHING_METHOD_OPTIONS, values=constants.VALID_MATCHING_METHODS,
|
|
432
552
|
default=constants.DEFAULT_MATCHING_METHOD)
|
|
@@ -434,46 +554,45 @@ class AlignFramesConfigurator(DefaultActionConfigurator):
|
|
|
434
554
|
"SIFT: Requires SIFT descriptor and K-NN matching\n"
|
|
435
555
|
"ORB/AKAZE: Work best with Hamming distance"
|
|
436
556
|
)
|
|
437
|
-
|
|
438
557
|
self.descriptor_field.setToolTip(
|
|
439
558
|
"SIFT: Requires K-NN matching\n"
|
|
440
559
|
"ORB/AKAZE: Require Hamming distance with ORB/AKAZE detectors"
|
|
441
560
|
)
|
|
442
|
-
|
|
443
561
|
self.matching_method_field.setToolTip(
|
|
444
562
|
"Automatically selected based on detector/descriptor combination"
|
|
445
563
|
)
|
|
446
|
-
|
|
447
564
|
self.detector_field.currentIndexChanged.connect(self.change_match_config)
|
|
448
565
|
self.descriptor_field.currentIndexChanged.connect(self.change_match_config)
|
|
449
566
|
self.matching_method_field.currentIndexChanged.connect(self.change_match_config)
|
|
450
|
-
self.
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
self.
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
567
|
+
self.add_field(
|
|
568
|
+
'flann_idx_kdtree', FIELD_INT, 'Flann idx kdtree', required=False,
|
|
569
|
+
default=constants.DEFAULT_FLANN_IDX_KDTREE,
|
|
570
|
+
min_val=0, max_val=10)
|
|
571
|
+
self.add_field(
|
|
572
|
+
'flann_trees', FIELD_INT, 'Flann trees', required=False,
|
|
573
|
+
default=constants.DEFAULT_FLANN_TREES,
|
|
574
|
+
min_val=0, max_val=10)
|
|
575
|
+
self.add_field(
|
|
576
|
+
'flann_checks', FIELD_INT, 'Flann checks', required=False,
|
|
577
|
+
default=constants.DEFAULT_FLANN_CHECKS,
|
|
578
|
+
min_val=0, max_val=1000)
|
|
579
|
+
self.add_field(
|
|
580
|
+
'threshold', FIELD_FLOAT, 'Threshold', required=False,
|
|
581
|
+
default=constants.DEFAULT_ALIGN_THRESHOLD,
|
|
582
|
+
min_val=0, max_val=1, step=0.05)
|
|
464
583
|
self.add_bold_label("Transform:")
|
|
465
|
-
transform = self.
|
|
584
|
+
transform = self.add_field(
|
|
466
585
|
'transform', FIELD_COMBO, 'Transform', required=False,
|
|
467
586
|
options=self.TRANSFORM_OPTIONS, values=constants.VALID_TRANSFORMS,
|
|
468
587
|
default=constants.DEFAULT_TRANSFORM)
|
|
469
|
-
method = self.
|
|
588
|
+
method = self.add_field(
|
|
470
589
|
'align_method', FIELD_COMBO, 'Align method', required=False,
|
|
471
590
|
options=self.METHOD_OPTIONS, values=constants.VALID_ALIGN_METHODS,
|
|
472
591
|
default=constants.DEFAULT_ALIGN_METHOD)
|
|
473
|
-
rans_threshold = self.
|
|
592
|
+
rans_threshold = self.add_field(
|
|
474
593
|
'rans_threshold', FIELD_FLOAT, 'RANSAC threshold (px)', required=False,
|
|
475
594
|
default=constants.DEFAULT_RANS_THRESHOLD, min_val=0, max_val=20, step=0.1)
|
|
476
|
-
self.
|
|
595
|
+
self.add_field(
|
|
477
596
|
'min_good_matches', FIELD_INT, "Min. good matches", required=False,
|
|
478
597
|
default=constants.DEFAULT_ALIGN_MIN_GOOD_MATCHES, min_val=0, max_val=500)
|
|
479
598
|
|
|
@@ -483,17 +602,19 @@ class AlignFramesConfigurator(DefaultActionConfigurator):
|
|
|
483
602
|
rans_threshold.setEnabled(True)
|
|
484
603
|
elif text == self.METHOD_OPTIONS[1]:
|
|
485
604
|
rans_threshold.setEnabled(False)
|
|
605
|
+
|
|
486
606
|
method.currentIndexChanged.connect(change_method)
|
|
487
607
|
change_method()
|
|
488
|
-
self.
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
608
|
+
self.add_field(
|
|
609
|
+
'align_confidence', FIELD_FLOAT, 'Confidence (%)',
|
|
610
|
+
required=False, decimals=1,
|
|
611
|
+
default=constants.DEFAULT_ALIGN_CONFIDENCE,
|
|
612
|
+
min_val=70.0, max_val=100.0, step=0.1)
|
|
492
613
|
|
|
493
|
-
refine_iters = self.
|
|
614
|
+
refine_iters = self.add_field(
|
|
494
615
|
'refine_iters', FIELD_INT, 'Refinement iterations (Rigid)', required=False,
|
|
495
616
|
default=constants.DEFAULT_REFINE_ITERS, min_val=0, max_val=1000)
|
|
496
|
-
max_iters = self.
|
|
617
|
+
max_iters = self.add_field(
|
|
497
618
|
'max_iters', FIELD_INT, 'Max. iterations (Homography)', required=False,
|
|
498
619
|
default=constants.DEFAULT_ALIGN_MAX_ITERS, min_val=0, max_val=5000)
|
|
499
620
|
|
|
@@ -505,37 +626,36 @@ class AlignFramesConfigurator(DefaultActionConfigurator):
|
|
|
505
626
|
elif text == self.TRANSFORM_OPTIONS[1]:
|
|
506
627
|
refine_iters.setEnabled(False)
|
|
507
628
|
max_iters.setEnabled(True)
|
|
629
|
+
|
|
508
630
|
transform.currentIndexChanged.connect(change_transform)
|
|
509
631
|
change_transform()
|
|
510
|
-
|
|
511
|
-
'
|
|
512
|
-
default=constants.
|
|
513
|
-
|
|
514
|
-
'fast_subsampling', FIELD_BOOL, 'Fast subsampling', required=False,
|
|
515
|
-
default=constants.DEFAULT_ALIGN_FAST_SUBSAMPLING)
|
|
516
|
-
|
|
517
|
-
def change_subsample():
|
|
518
|
-
fast_subsampling.setEnabled(subsample.value() > 1)
|
|
519
|
-
subsample.valueChanged.connect(change_subsample)
|
|
520
|
-
change_subsample()
|
|
632
|
+
self.add_field(
|
|
633
|
+
'abort_abnormal', FIELD_BOOL, 'Abort on abnormal transf.',
|
|
634
|
+
required=False, default=constants.DEFAULT_ALIGN_ABORT_ABNORMAL)
|
|
635
|
+
self.add_subsample_fields()
|
|
521
636
|
self.add_bold_label("Border:")
|
|
522
|
-
self.
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
637
|
+
self.add_field(
|
|
638
|
+
'border_mode', FIELD_COMBO, 'Border mode', required=False,
|
|
639
|
+
options=self.BORDER_MODE_OPTIONS,
|
|
640
|
+
values=constants.VALID_BORDER_MODES,
|
|
641
|
+
default=constants.DEFAULT_BORDER_MODE)
|
|
642
|
+
self.add_field(
|
|
643
|
+
'border_value', FIELD_INT_TUPLE,
|
|
644
|
+
'Border value (if constant)', required=False, size=4,
|
|
645
|
+
default=constants.DEFAULT_BORDER_VALUE,
|
|
646
|
+
labels=constants.RGBA_LABELS,
|
|
647
|
+
min_val=constants.DEFAULT_BORDER_VALUE, max_val=[255] * 4)
|
|
648
|
+
self.add_field(
|
|
649
|
+
'border_blur', FIELD_FLOAT, 'Border blur', required=False,
|
|
650
|
+
default=constants.DEFAULT_BORDER_BLUR,
|
|
651
|
+
min_val=0, max_val=1000, step=1)
|
|
534
652
|
self.add_bold_label("Miscellanea:")
|
|
535
|
-
self.
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
653
|
+
self.add_field(
|
|
654
|
+
'plot_summary', FIELD_BOOL, 'Plot summary',
|
|
655
|
+
required=False, default=False)
|
|
656
|
+
self.add_field(
|
|
657
|
+
'plot_matches', FIELD_BOOL, 'Plot matches',
|
|
658
|
+
required=False, default=False)
|
|
539
659
|
|
|
540
660
|
def update_params(self, params: Dict[str, Any]) -> bool:
|
|
541
661
|
if self.detector_field and self.descriptor_field and self.matching_method_field:
|
|
@@ -555,35 +675,59 @@ class AlignFramesConfigurator(DefaultActionConfigurator):
|
|
|
555
675
|
return super().update_params(params)
|
|
556
676
|
|
|
557
677
|
|
|
558
|
-
class BalanceFramesConfigurator(
|
|
678
|
+
class BalanceFramesConfigurator(SubsampleActionConfigurator):
|
|
559
679
|
CORRECTION_MAP_OPTIONS = ['Linear', 'Gamma', 'Match histograms']
|
|
560
|
-
CHANNEL_OPTIONS = ['Luminosity', 'RGB', 'HSV', 'HLS']
|
|
680
|
+
CHANNEL_OPTIONS = ['Luminosity', 'RGB', 'HSV', 'HLS', 'LAB']
|
|
681
|
+
|
|
682
|
+
def create_form(self, layout, action):
|
|
683
|
+
super().create_form(layout, action)
|
|
684
|
+
if self.expert:
|
|
685
|
+
self.add_field(
|
|
686
|
+
'mask_size', FIELD_FLOAT, 'Mask size', required=False,
|
|
687
|
+
default=0, min_val=0, max_val=5, step=0.1)
|
|
688
|
+
self.add_field(
|
|
689
|
+
'intensity_interval', FIELD_INT_TUPLE, 'Intensity range',
|
|
690
|
+
required=False, size=2,
|
|
691
|
+
default=[v for k, v in constants.DEFAULT_INTENSITY_INTERVAL.items()],
|
|
692
|
+
labels=['min', 'max'], min_val=[-1] * 2, max_val=[65536] * 2)
|
|
693
|
+
self.add_subsample_fields()
|
|
694
|
+
self.add_field(
|
|
695
|
+
'corr_map', FIELD_COMBO, 'Correction map', required=False,
|
|
696
|
+
options=self.CORRECTION_MAP_OPTIONS, values=constants.VALID_BALANCE,
|
|
697
|
+
default='Linear')
|
|
698
|
+
self.add_field(
|
|
699
|
+
'channel', FIELD_COMBO, 'Channel', required=False,
|
|
700
|
+
options=self.CHANNEL_OPTIONS, values=constants.VALID_BALANCE_CHANNELS,
|
|
701
|
+
default='Luminosity')
|
|
702
|
+
self.add_bold_label("Miscellanea:")
|
|
703
|
+
self.add_field(
|
|
704
|
+
'plot_summary', FIELD_BOOL, 'Plot summary',
|
|
705
|
+
required=False, default=False)
|
|
706
|
+
self.add_field(
|
|
707
|
+
'plot_histograms', FIELD_BOOL, 'Plot histograms',
|
|
708
|
+
required=False, default=False)
|
|
709
|
+
|
|
561
710
|
|
|
711
|
+
class VignettingConfigurator(SubsampleActionConfigurator):
|
|
562
712
|
def create_form(self, layout, action):
|
|
563
713
|
super().create_form(layout, action)
|
|
564
714
|
if self.expert:
|
|
565
|
-
self.
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
self.
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
default=constants.DEFAULT_BALANCE_FAST_SUBSAMPLING)
|
|
578
|
-
self.builder.add_field('corr_map', FIELD_COMBO, 'Correction map', required=False,
|
|
579
|
-
options=self.CORRECTION_MAP_OPTIONS, values=constants.VALID_BALANCE,
|
|
580
|
-
default='Linear')
|
|
581
|
-
self.builder.add_field('channel', FIELD_COMBO, 'Channel', required=False,
|
|
582
|
-
options=self.CHANNEL_OPTIONS,
|
|
583
|
-
values=constants.VALID_BALANCE_CHANNELS,
|
|
584
|
-
default='Luminosity')
|
|
715
|
+
self.add_field(
|
|
716
|
+
'r_steps', FIELD_INT, 'Radial steps', required=False,
|
|
717
|
+
default=constants.DEFAULT_R_STEPS, min_val=1, max_val=1000)
|
|
718
|
+
self.add_field(
|
|
719
|
+
'black_threshold', FIELD_INT, 'Black intensity threshold',
|
|
720
|
+
required=False, default=constants.DEFAULT_BLACK_THRESHOLD,
|
|
721
|
+
min_val=0, max_val=1000)
|
|
722
|
+
self.add_subsample_fields()
|
|
723
|
+
self.add_field(
|
|
724
|
+
'max_correction', FIELD_FLOAT, 'Max. correction', required=False,
|
|
725
|
+
default=constants.DEFAULT_MAX_CORRECTION,
|
|
726
|
+
min_val=0, max_val=1, step=0.05)
|
|
585
727
|
self.add_bold_label("Miscellanea:")
|
|
586
|
-
self.
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
728
|
+
self.add_field(
|
|
729
|
+
'plot_correction', FIELD_BOOL, 'Plot correction', required=False,
|
|
730
|
+
default=False)
|
|
731
|
+
self.add_field(
|
|
732
|
+
'plot_summary', FIELD_BOOL, 'Plot summary', required=False,
|
|
733
|
+
default=False)
|