shinestacker 1.8.0__py3-none-any.whl → 1.8.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.

@@ -191,7 +191,8 @@ class ImageSequenceManager:
191
191
 
192
192
 
193
193
  class ReferenceFrameTask(SequentialTask, ImageSequenceManager):
194
- def __init__(self, name, enabled=True, reference_index=0, step_process=False, **kwargs):
194
+ def __init__(self, name, enabled=True, reference_index=0,
195
+ step_process=constants.DEFAULT_COMBINED_ACTIONS_STEP_PROCESS, **kwargs):
195
196
  ImageSequenceManager.__init__(self, name, **kwargs)
196
197
  SequentialTask.__init__(self, name, enabled)
197
198
  self.ref_idx = reference_index
@@ -276,7 +277,8 @@ class SubAction:
276
277
 
277
278
  class CombinedActions(ReferenceFrameTask):
278
279
  def __init__(self, name, actions=[], enabled=True, **kwargs):
279
- ReferenceFrameTask.__init__(self, name, enabled, **kwargs)
280
+ step_process = kwargs.pop('step_process', constants.DEFAULT_COMBINED_ACTIONS_STEP_PROCESS)
281
+ ReferenceFrameTask.__init__(self, name, enabled, step_process=step_process, **kwargs)
280
282
  self._actions = actions
281
283
  self._metadata = (None, None)
282
284
 
@@ -294,11 +296,15 @@ class CombinedActions(ReferenceFrameTask):
294
296
  self._metadata = get_img_metadata(img)
295
297
  return img
296
298
 
299
+ def frame_str(self, idx=-1):
300
+ if self.run_sequential():
301
+ idx = self.current_action_count
302
+ return f"frame {idx + 1}/{self.total_action_counts}"
303
+
297
304
  def run_frame(self, idx, ref_idx):
298
305
  input_path = self.input_filepath(idx)
299
306
  self.print_message(
300
- color_str(f'read input image '
301
- f'{idx + 1}/{self.total_action_counts}, '
307
+ color_str(f'read input {self.frame_str(idx)}, '
302
308
  f'{os.path.basename(input_path)}', constants.LOG_COLOR_LEVEL_3))
303
309
  img = read_img(input_path)
304
310
  validate_image(img, *(self._metadata))
@@ -317,20 +323,19 @@ class CombinedActions(ReferenceFrameTask):
317
323
  if img is not None:
318
324
  img = a.run_frame(idx, ref_idx, img)
319
325
  else:
320
- self.sub_message(
321
- color_str(": null input received, action skipped",
326
+ self.print_message(
327
+ color_str("null input received, action skipped",
322
328
  constants.LOG_COLOR_ALERT),
323
329
  level=logging.WARNING)
324
330
  if img is not None:
325
331
  output_path = os.path.join(self.output_full_path(), os.path.basename(input_path))
326
332
  self.print_message(
327
- color_str(f'write output image '
328
- f'{idx + 1}/{self.total_action_counts}, '
333
+ color_str(f'write output {self.frame_str(idx)}, '
329
334
  f'{os.path.basename(output_path)}', constants.LOG_COLOR_LEVEL_3))
330
335
  write_img(output_path, img)
331
336
  return img
332
337
  self.print_message(color_str(
333
- f"no output file resulted from processing input file: {os.path.basename(input_path)}",
338
+ f"no output resulted from processing input file: {os.path.basename(input_path)}",
334
339
  constants.LOG_COLOR_ALERT), level=logging.WARNING)
335
340
  return None
336
341
 
@@ -4,7 +4,7 @@ import traceback
4
4
  import logging
5
5
  import numpy as np
6
6
  import matplotlib.pyplot as plt
7
- from scipy.optimize import curve_fit, fsolve
7
+ from scipy.optimize import curve_fit, bisect
8
8
  import cv2
9
9
  from .. core.colors import color_str
10
10
  from .. core.core_utils import setup_matplotlib_mode
@@ -181,9 +181,22 @@ class Vignetting(SubAction):
181
181
  self.process.callback(
182
182
  constants.CALLBACK_SAVE_PLOT, self.process.id,
183
183
  f"{self.process.name}: intensity\nframe {idx_str}", plot_path)
184
+
184
185
  for i, p in enumerate(self.percentiles):
185
- self.corrections[i][idx] = fsolve(lambda x: sigmoid_model(x, *params) /
186
- self.v0 - p, r0_fit)[0]
186
+ s1 = sigmoid_model(0, *params) / self.v0
187
+ s2 = sigmoid_model(self.r_max, *params) / self.v0
188
+ if s1 > p and s2 < p:
189
+ try:
190
+ c = bisect(lambda x: sigmoid_model(x, *params) / self.v0 - p, 0, self.r_max)
191
+ except Exception as e:
192
+ traceback.print_tb(e.__traceback__)
193
+ self.process.sub_message(color_str(f": {str(e).lower()}", "yellow"),
194
+ level=logging.WARNING)
195
+ elif s1 <= p:
196
+ c = 0
197
+ else:
198
+ c = self.r_max
199
+ self.corrections[i][idx] = c
187
200
  self.process.print_message(
188
201
  color_str(f"{self.process.idx_tot_str(idx)}: correct vignetting", "cyan"))
189
202
  return correct_vignetting(
@@ -1,13 +1,174 @@
1
- # pylint: disable=C0114, C0115, C0116, E0611, W0718, R0903, E0611, R0902
1
+ # pylint: disable=C0114, C0115, C0116, E0611, R0913, R0917, E1121
2
+ from abc import ABC, abstractmethod
2
3
  from PySide6.QtCore import Signal
3
- from PySide6.QtWidgets import QFrame, QLabel, QCheckBox, QComboBox, QDoubleSpinBox, QSpinBox
4
+ from PySide6.QtWidgets import QLabel, QCheckBox, QComboBox, QDoubleSpinBox, QSpinBox
4
5
  from .. config.settings import Settings
5
6
  from .. config.constants import constants
6
7
  from .. config.gui_constants import gui_constants
7
8
  from .. gui.config_dialog import ConfigDialog
9
+ from .. gui.action_config import add_tab, create_tab_widget
8
10
  from .. gui.action_config_dialog import AlignFramesConfigBase
9
11
 
10
12
 
13
+ class BaseParameter(ABC):
14
+ def __init__(self, key, label, tooltip=""):
15
+ self.key = key
16
+ self.label = label
17
+ self.tooltip = tooltip
18
+ self.widget = None
19
+
20
+ @abstractmethod
21
+ def create_widget(self, parent):
22
+ pass
23
+
24
+ @abstractmethod
25
+ def get_value(self):
26
+ pass
27
+
28
+ @abstractmethod
29
+ def set_value(self, value):
30
+ pass
31
+
32
+ @abstractmethod
33
+ def set_default(self):
34
+ pass
35
+
36
+
37
+ class NestedParameter(BaseParameter):
38
+ def __init__(self, parent_key, key, label, tooltip=""):
39
+ super().__init__(key, label, tooltip)
40
+ self.parent_key = parent_key
41
+
42
+ def get_nested_value(self, settings):
43
+ return settings.get(self.parent_key).get(self.key)
44
+
45
+ def set_nested_value(self, settings, value):
46
+ nested_dict = settings.get(self.parent_key).copy()
47
+ nested_dict[self.key] = value
48
+ settings.set(self.parent_key, nested_dict)
49
+
50
+
51
+ class CheckBoxParameter(BaseParameter):
52
+ def __init__(self, key, label, default_value, tooltip=""):
53
+ super().__init__(key, label, tooltip)
54
+ self.default_value = default_value
55
+
56
+ def create_widget(self, parent):
57
+ self.widget = QCheckBox(parent)
58
+ if self.tooltip:
59
+ self.widget.setToolTip(self.tooltip)
60
+ return self.widget
61
+
62
+ def get_value(self):
63
+ return self.widget.isChecked()
64
+
65
+ def set_value(self, value):
66
+ self.widget.setChecked(value)
67
+
68
+ def set_default(self):
69
+ self.widget.setChecked(self.default_value)
70
+
71
+
72
+ class SpinBoxParameter(BaseParameter):
73
+ def __init__(self, key, label, default_value, min_val, max_val, step=1, tooltip=""):
74
+ super().__init__(key, label, tooltip)
75
+ self.default_value = default_value
76
+ self.min_val = min_val
77
+ self.max_val = max_val
78
+ self.step = step
79
+
80
+ def create_widget(self, parent):
81
+ self.widget = QSpinBox(parent)
82
+ self.widget.setRange(self.min_val, self.max_val)
83
+ self.widget.setSingleStep(self.step)
84
+ if self.tooltip:
85
+ self.widget.setToolTip(self.tooltip)
86
+ return self.widget
87
+
88
+ def get_value(self):
89
+ return self.widget.value()
90
+
91
+ def set_value(self, value):
92
+ self.widget.setValue(value)
93
+
94
+ def set_default(self):
95
+ self.widget.setValue(self.default_value)
96
+
97
+
98
+ class DoubleSpinBoxParameter(SpinBoxParameter):
99
+ def create_widget(self, parent):
100
+ self.widget = QDoubleSpinBox(parent)
101
+ self.widget.setRange(self.min_val, self.max_val)
102
+ self.widget.setSingleStep(self.step)
103
+ if self.tooltip:
104
+ self.widget.setToolTip(self.tooltip)
105
+ return self.widget
106
+
107
+
108
+ class ComboBoxParameter(BaseParameter):
109
+ def __init__(self, key, label, default_value, options, tooltip=""):
110
+ super().__init__(key, label, tooltip)
111
+ self.default_value = default_value
112
+ self.options = options
113
+
114
+ def create_widget(self, parent):
115
+ self.widget = QComboBox(parent)
116
+ for display_text, data in self.options:
117
+ self.widget.addItem(display_text, data)
118
+ if self.tooltip:
119
+ self.widget.setToolTip(self.tooltip)
120
+ return self.widget
121
+
122
+ def get_value(self):
123
+ return self.widget.itemData(self.widget.currentIndex())
124
+
125
+ def set_value(self, value):
126
+ idx = self.widget.findData(value)
127
+ if idx >= 0:
128
+ self.widget.setCurrentIndex(idx)
129
+
130
+ def set_default(self):
131
+ idx = self.widget.findData(self.default_value)
132
+ if idx >= 0:
133
+ self.widget.setCurrentIndex(idx)
134
+
135
+
136
+ class CallbackComboBoxParameter(ComboBoxParameter):
137
+ def __init__(self, key, label, default_value, options, tooltip="", on_change=None):
138
+ super().__init__(key, label, default_value, options, tooltip)
139
+ self.on_change = on_change
140
+
141
+ def create_widget(self, parent):
142
+ widget = super().create_widget(parent)
143
+ if self.on_change:
144
+ widget.currentIndexChanged.connect(self.on_change)
145
+ return widget
146
+
147
+
148
+ class NestedSpinBoxParameter(SpinBoxParameter, NestedParameter):
149
+ def __init__(self, parent_key, key, label, default_value, min_val, max_val, step=1, tooltip=""):
150
+ SpinBoxParameter.__init__(
151
+ self, key, label, default_value, min_val, max_val, step, tooltip)
152
+ NestedParameter.__init__(
153
+ self, parent_key, key, label, tooltip)
154
+
155
+
156
+ class NestedDoubleSpinBoxParameter(DoubleSpinBoxParameter, NestedParameter):
157
+ def __init__(self, parent_key, key, label, default_value, min_val, max_val, step=1, tooltip=""):
158
+ DoubleSpinBoxParameter.__init__(
159
+ self, key, label, default_value, min_val, max_val, step, tooltip)
160
+ NestedParameter.__init__(
161
+ self, parent_key, key, label, tooltip)
162
+
163
+
164
+ class NestedCallbackComboBoxParameter(CallbackComboBoxParameter, NestedParameter):
165
+ def __init__(self, parent_key, key, label, default_value,
166
+ options, tooltip="", on_change=None):
167
+ CallbackComboBoxParameter.__init__(
168
+ self, key, label, default_value, options, tooltip, on_change)
169
+ NestedParameter.__init__(self, parent_key, key, label, tooltip)
170
+
171
+
11
172
  class SettingsDialog(ConfigDialog, AlignFramesConfigBase):
12
173
  update_project_config_requested = Signal()
13
174
  update_retouch_config_requested = Signal()
@@ -17,163 +178,144 @@ class SettingsDialog(ConfigDialog, AlignFramesConfigBase):
17
178
  self.project_settings = project_settings
18
179
  self.retouch_settings = retouch_settings
19
180
  self.settings = Settings.instance()
20
- self.expert_options = None
21
- self.combined_actions_max_threads = None
22
- self.align_frames_max_threads = None
23
- self.detector = None
24
- self.descriptor = None
25
- self.matching_method = None
26
- self.focus_stack_max_threads = None
27
- self.view_strategy = None
28
- self.min_mouse_step_brush_fraction = None
29
- self.paint_refresh_time = None
30
- self.display_refresh_time = None
31
- self.cursor_update_time = None
181
+ self.project_parameters = []
182
+ self.retouch_parameters = []
183
+ self._init_parameters()
32
184
  super().__init__("Settings", parent)
33
185
 
186
+ def _init_parameters(self):
187
+ if self.project_settings:
188
+ self.project_parameters = [
189
+ CheckBoxParameter(
190
+ 'expert_options', 'Expert options:',
191
+ constants.DEFAULT_EXPERT_OPTIONS),
192
+ NestedSpinBoxParameter(
193
+ 'combined_actions_params', 'max_threads',
194
+ 'Combined actions, max num. of cores:',
195
+ constants.DEFAULT_FWK_MAX_THREADS, 0, 64),
196
+ NestedDoubleSpinBoxParameter(
197
+ 'align_frames_params', 'memory_limit',
198
+ 'Align frames, mem. limit (approx., GBytes):',
199
+ constants.DEFAULT_ALIGN_MEMORY_LIMIT_GB, 1.0, 64.0, 1.0),
200
+ NestedSpinBoxParameter(
201
+ 'align_frames_params', 'max_threads',
202
+ 'Align frames, max num. of cores:',
203
+ constants.DEFAULT_ALIGN_MAX_THREADS, 0, 64),
204
+ NestedCallbackComboBoxParameter(
205
+ 'align_frames_params', 'detector', 'Detector:',
206
+ constants.DEFAULT_DETECTOR, [(d, d) for d in constants.VALID_DETECTORS],
207
+ tooltip=self.DETECTOR_DESCRIPTOR_TOOLTIPS['detector'],
208
+ on_change=self.change_match_config_settings),
209
+ NestedCallbackComboBoxParameter(
210
+ 'align_frames_params', 'descriptor', 'Descriptor:',
211
+ constants.DEFAULT_DESCRIPTOR, [(d, d) for d in constants.VALID_DESCRIPTORS],
212
+ tooltip=self.DETECTOR_DESCRIPTOR_TOOLTIPS['descriptor'],
213
+ on_change=self.change_match_config_settings),
214
+ NestedCallbackComboBoxParameter(
215
+ 'align_frames_params', 'match_method', 'Match method:',
216
+ constants.DEFAULT_MATCHING_METHOD,
217
+ list(zip(self.MATCHING_METHOD_OPTIONS, constants.VALID_MATCHING_METHODS)),
218
+ tooltip=self.DETECTOR_DESCRIPTOR_TOOLTIPS['match_method'],
219
+ on_change=self.change_match_config_settings),
220
+ NestedDoubleSpinBoxParameter(
221
+ 'focus_stack_params', 'memory_limit',
222
+ 'Focus stacking, mem. limit (approx., GBytes):',
223
+ constants.DEFAULT_PY_MEMORY_LIMIT_GB, 1.0, 64.0, 1.0),
224
+ NestedSpinBoxParameter(
225
+ 'focus_stack_params', 'max_threads', 'Focus stacking, max. num. of cores:',
226
+ constants.DEFAULT_PY_MAX_THREADS, 0, 64),
227
+ ]
228
+ if self.retouch_settings:
229
+ self.retouch_parameters = [
230
+ ComboBoxParameter(
231
+ 'view_strategy', 'View strategy:',
232
+ constants.DEFAULT_VIEW_STRATEGY,
233
+ [
234
+ ("Overlaid", "overlaid"),
235
+ ("Side by side", "sidebyside"),
236
+ ("Top-Bottom", "topbottom")
237
+ ]),
238
+ DoubleSpinBoxParameter(
239
+ 'min_mouse_step_brush_fraction', 'Min. mouse step in brush units:',
240
+ gui_constants.DEFAULT_MIN_MOUSE_STEP_BRUSH_FRACTION, 0, 1, 0.02),
241
+ SpinBoxParameter(
242
+ 'paint_refresh_time', 'Paint refresh time:',
243
+ gui_constants.DEFAULT_PAINT_REFRESH_TIME, 0, 1000),
244
+ SpinBoxParameter(
245
+ 'display_refresh_time', 'Display refresh time:',
246
+ gui_constants.DEFAULT_DISPLAY_REFRESH_TIME, 0, 200),
247
+ SpinBoxParameter(
248
+ 'cursor_update_time', 'Cursor refresh time:',
249
+ gui_constants.DEFAULT_CURSOR_UPDATE_TIME, 0, 50),
250
+ ]
251
+
34
252
  def create_form_content(self):
253
+ self.tab_widget = create_tab_widget(self.container_layout)
35
254
  if self.project_settings:
36
- self.create_project_settings()
37
- separator = QFrame()
38
- separator.setFrameShape(QFrame.HLine)
39
- separator.setFrameShadow(QFrame.Sunken)
40
- separator.setLineWidth(1)
41
- self.container_layout.addRow(separator)
255
+ project_tab_layout = add_tab(self.tab_widget, "Project Settings")
256
+ self.create_project_settings(project_tab_layout)
42
257
  if self.retouch_settings:
43
- self.create_retouch_settings()
258
+ retouch_tab_layout = add_tab(self.tab_widget, "Retouch Settings")
259
+ self.create_retouch_settings(retouch_tab_layout)
44
260
 
45
- def create_project_settings(self):
46
- label = QLabel("Project settings")
261
+ def create_project_settings(self, layout=None):
262
+ if layout is None:
263
+ layout = self.container_layout
264
+ label = QLabel("Project settings:")
47
265
  label.setStyleSheet("font-weight: bold")
48
- self.container_layout.addRow(label)
49
- self.expert_options = QCheckBox()
50
- self.expert_options.setChecked(self.settings.get('expert_options'))
51
- self.container_layout.addRow("Expert options:", self.expert_options)
52
- self.combined_actions_max_threads = QSpinBox()
53
- self.combined_actions_max_threads.setRange(0, 64)
54
- self.combined_actions_max_threads.setValue(
55
- self.settings.get('combined_actions_params')['max_threads'])
56
- self.container_layout.addRow("Max num. of cores, combined actions:",
57
- self.combined_actions_max_threads)
58
-
59
- self.align_frames_max_threads = QSpinBox()
60
- self.align_frames_max_threads.setRange(0, 64)
61
- self.align_frames_max_threads.setValue(
62
- self.settings.get('align_frames_params')['max_threads'])
63
- self.container_layout.addRow("Max num. of cores, align frames:",
64
- self.align_frames_max_threads)
65
-
66
- def change_match_config():
67
- self.change_match_config(
68
- self.detector, self.descriptor,
69
- self. matching_method, self.show_info)
70
-
71
- self.detector = QComboBox()
72
- self.detector.addItems(constants.VALID_DETECTORS)
73
- self.descriptor = QComboBox()
74
- self.descriptor.addItems(constants.VALID_DESCRIPTORS)
75
- self.matching_method = QComboBox()
266
+ layout.addRow(label)
267
+ for param in self.project_parameters:
268
+ widget = param.create_widget(self)
269
+ param.set_value(self._get_current_value(param))
270
+ layout.addRow(param.label, widget)
76
271
  self.info_label = QLabel()
77
272
  self.info_label.setStyleSheet("color: orange; font-style: italic;")
78
- self.matching_method = QComboBox()
79
- for k, v in zip(self.MATCHING_METHOD_OPTIONS, constants.VALID_MATCHING_METHODS):
80
- self.matching_method.addItem(k, v)
81
- self.detector.setToolTip(self.DETECTOR_DESCRIPTOR_TOOLTIPS['detector'])
82
- self.descriptor.setToolTip(self.DETECTOR_DESCRIPTOR_TOOLTIPS['descriptor'])
83
- self.matching_method.setToolTip(self.DETECTOR_DESCRIPTOR_TOOLTIPS['match_method'])
84
- self.detector.currentIndexChanged.connect(change_match_config)
85
- self.descriptor.currentIndexChanged.connect(change_match_config)
86
- self.matching_method.currentIndexChanged.connect(change_match_config)
87
- self.container_layout.addRow('Detector:', self.detector)
88
- self.container_layout.addRow('Descriptor:', self.descriptor)
89
- self.container_layout.addRow(self.info_label)
90
- self.container_layout.addRow('Match method:', self.matching_method)
91
-
92
- self.focus_stack_max_threads = QSpinBox()
93
- self.focus_stack_max_threads.setRange(0, 64)
94
- self.focus_stack_max_threads.setValue(
95
- self.settings.get('align_frames_params')['max_threads'])
96
- self.container_layout.addRow("Max num. of cores, focus stacking:",
97
- self.focus_stack_max_threads)
98
-
99
- def create_retouch_settings(self):
100
- label = QLabel("Retouch settings")
273
+ layout.addRow(self.info_label)
274
+
275
+ def create_retouch_settings(self, layout=None):
276
+ if layout is None:
277
+ layout = self.container_layout
278
+ label = QLabel("Retouch settings:")
101
279
  label.setStyleSheet("font-weight: bold")
102
- self.container_layout.addRow(label)
103
- self.view_strategy = QComboBox()
104
- self.view_strategy.addItem("Overlaid", "overlaid")
105
- self.view_strategy.addItem("Side by side", "sidebyside")
106
- self.view_strategy.addItem("Top-Bottom", "topbottom")
107
- idx = self.view_strategy.findData(self.settings.get('view_strategy'))
108
- if idx >= 0:
109
- self.view_strategy.setCurrentIndex(idx)
110
- self.container_layout.addRow("View strategy:", self.view_strategy)
111
- self.min_mouse_step_brush_fraction = QDoubleSpinBox()
112
- self.min_mouse_step_brush_fraction.setValue(
113
- self.settings.get('min_mouse_step_brush_fraction'))
114
- self.min_mouse_step_brush_fraction.setRange(0, 1)
115
- self.min_mouse_step_brush_fraction.setDecimals(2)
116
- self.min_mouse_step_brush_fraction.setSingleStep(0.02)
117
- self.container_layout.addRow("Min. mouse step in brush units:",
118
- self.min_mouse_step_brush_fraction)
119
- self.paint_refresh_time = QSpinBox()
120
- self.paint_refresh_time.setRange(0, 1000)
121
- self.paint_refresh_time.setValue(
122
- self.settings.get('paint_refresh_time'))
123
- self.container_layout.addRow("Paint refresh time:",
124
- self.paint_refresh_time)
125
- self.display_refresh_time = QSpinBox()
126
- self.display_refresh_time.setRange(0, 200)
127
- self.display_refresh_time.setValue(
128
- self.settings.get('display_refresh_time'))
129
- self.container_layout.addRow("Display refresh time:",
130
- self.display_refresh_time)
131
-
132
- self.cursor_update_time = QSpinBox()
133
- self.cursor_update_time.setRange(0, 50)
134
- self.cursor_update_time.setValue(
135
- self.settings.get('cursor_update_time'))
136
- self.container_layout.addRow("Cursor refresh time:",
137
- self.cursor_update_time)
280
+ layout.addRow(label)
281
+ for param in self.retouch_parameters:
282
+ widget = param.create_widget(self)
283
+ param.set_value(self._get_current_value(param))
284
+ layout.addRow(param.label, widget)
285
+
286
+ def _get_current_value(self, param):
287
+ if isinstance(param, NestedParameter):
288
+ return param.get_nested_value(self.settings)
289
+ return self.settings.get(param.key)
290
+
291
+ def _set_current_value(self, param, value):
292
+ if isinstance(param, NestedParameter):
293
+ param.set_nested_value(self.settings, value)
294
+ else:
295
+ self.settings.set(param.key, value)
296
+
297
+ def change_match_config_settings(self):
298
+ detector_widget = None
299
+ descriptor_widget = None
300
+ matching_method_widget = None
301
+ for param in self.project_parameters:
302
+ if (isinstance(param, NestedParameter) and
303
+ param.parent_key == 'align_frames_params'):
304
+ if param.key == 'detector':
305
+ detector_widget = param.widget
306
+ elif param.key == 'descriptor':
307
+ descriptor_widget = param.widget
308
+ elif param.key == 'match_method':
309
+ matching_method_widget = param.widget
310
+ if detector_widget and descriptor_widget and matching_method_widget:
311
+ self.change_match_config(
312
+ detector_widget, descriptor_widget, matching_method_widget, self.show_info)
138
313
 
139
314
  def accept(self):
140
- if self.project_settings:
141
- self.settings.set(
142
- 'expert_options', self.expert_options.isChecked())
143
- self.settings.set(
144
- 'combined_actions_params', {
145
- 'max_threads': self.combined_actions_max_threads.value()
146
- })
147
- self.settings.set(
148
- 'align_frames_params', {
149
- 'max_threads':
150
- self.align_frames_max_threads.value(),
151
- 'detector':
152
- self.descriptor.currentText(),
153
- 'descriptor':
154
- self.descriptor.currentText(),
155
- 'match_method':
156
- self.matching_method.itemData(self.matching_method.currentIndex())
157
- })
158
- self.settings.set(
159
- 'focus_stack_params', {
160
- 'max_threads': self.focus_stack_max_threads.value()
161
- })
162
- self.settings.set(
163
- 'focus_stack_bunch:params', {
164
- 'max_threads': self.focus_stack_max_threads.value()
165
- })
166
- if self.retouch_settings:
167
- self.settings.set(
168
- 'view_strategy', self.view_strategy.itemData(self.view_strategy.currentIndex()))
169
- self.settings.set(
170
- 'min_mouse_step_brush_fraction', self.min_mouse_step_brush_fraction.value())
171
- self.settings.set(
172
- 'paint_refresh_time', self.paint_refresh_time.value())
173
- self.settings.set(
174
- 'display_refresh_time', self.display_refresh_time.value())
175
- self.settings.set(
176
- 'cursor_update_time', self.cursor_update_time.value())
315
+ for param in self.project_parameters:
316
+ self._set_current_value(param, param.get_value())
317
+ for param in self.retouch_parameters:
318
+ self._set_current_value(param, param.get_value())
177
319
  self.settings.update()
178
320
  if self.project_settings:
179
321
  self.update_project_config_requested.emit()
@@ -182,32 +324,14 @@ class SettingsDialog(ConfigDialog, AlignFramesConfigBase):
182
324
  super().accept()
183
325
 
184
326
  def reset_to_defaults(self):
185
- if self.project_settings:
186
- self.expert_options.setChecked(constants.DEFAULT_EXPERT_OPTIONS)
187
- self.combined_actions_max_threads.setValue(constants.DEFAULT_MAX_FWK_THREADS)
188
- self.align_frames_max_threads.setValue(constants.DEFAULT_ALIGN_MAX_THREADS)
189
- self.detector.setCurrentText(constants.DEFAULT_DETECTOR)
190
- self.descriptor.setCurrentText(constants.DEFAULT_DESCRIPTOR)
191
- idx = self.matching_method.findData(constants.DEFAULT_MATCHING_METHOD)
192
- if idx >= 0:
193
- self.matching_method.setCurrentIndex(idx)
194
- self.focus_stack_max_threads.setValue(constants.DEFAULT_PY_MAX_THREADS)
195
- if self.retouch_settings:
196
- idx = self.view_strategy.findData(constants.DEFAULT_VIEW_STRATEGY)
197
- if idx >= 0:
198
- self.view_strategy.setCurrentIndex(idx)
199
- self.min_mouse_step_brush_fraction.setValue(
200
- gui_constants.DEFAULT_MIN_MOUSE_STEP_BRUSH_FRACTION)
201
- self.paint_refresh_time.setValue(
202
- gui_constants.DEFAULT_PAINT_REFRESH_TIME)
203
- self.display_refresh_time.setValue(
204
- gui_constants.DEFAULT_DISPLAY_REFRESH_TIME)
205
- self.cursor_update_time.setValue(
206
- gui_constants.DEFAULT_CURSOR_UPDATE_TIME)
207
-
208
-
209
- def show_settings_dialog(parent, project_settings, retouch_settings,
210
- handle_project_config, handle_retouch_config):
327
+ for param in self.project_parameters:
328
+ param.set_default()
329
+ for param in self.retouch_parameters:
330
+ param.set_default()
331
+
332
+
333
+ def show_settings_dialog(
334
+ parent, project_settings, retouch_settings, handle_project_config, handle_retouch_config):
211
335
  dialog = SettingsDialog(parent, project_settings, retouch_settings)
212
336
  dialog.update_project_config_requested.connect(handle_project_config)
213
337
  dialog.update_retouch_config_requested.connect(handle_retouch_config)
@@ -89,9 +89,12 @@ class _Constants:
89
89
  DEFAULT_MULTILAYER_FILE_REVERSE_ORDER = True
90
90
  MULTILAYER_WARNING_MEM_GB = 1
91
91
 
92
+ DEFAULT_COMBINED_ACTIONS_STEP_PROCESS = True
93
+
92
94
  DEFAULT_PLOTS_PATH = 'plots'
93
- DEFAULT_MAX_FWK_THREADS = 8
94
- DEFAULT_MAX_FWK_CHUNK_SUBMIT = True
95
+ DEFAULT_FWK_MEMORY_LIMIT_GB = 8
96
+ DEFAULT_FWK_MAX_THREADS = 8
97
+ DEFAULT_FWK_CHUNK_SUBMIT = True
95
98
 
96
99
  FIELD_SUBSAMPLE_VALUES_1 = [2, 3, 4, 6, 8, 12, 16, 24, 32]
97
100
  FIELD_SUBSAMPLE_OPTIONS_1 = [f"1/{n} × 1/{n}" for n in FIELD_SUBSAMPLE_VALUES_1]
@@ -99,7 +102,7 @@ class _Constants:
99
102
  FIELD_SUBSAMPLE_OPTIONS = ['Auto', 'Full resolution'] + FIELD_SUBSAMPLE_OPTIONS_1
100
103
  FIELD_SUBSAMPLE_DEFAULT = FIELD_SUBSAMPLE_VALUES[0]
101
104
 
102
- DEFAULT_NOISE_MAP_FILENAME = "noise-map/hot_pixels.png"
105
+ DEFAULT_NOISE_MAP_FILENAME = "hot_pixels.png"
103
106
  DEFAULT_NOISE_MAX_FRAMES = 10
104
107
  DEFAULT_MN_KERNEL_SIZE = 3
105
108
  INTERPOLATE_MEAN = 'MEAN'
@@ -135,7 +138,7 @@ class _Constants:
135
138
  VALID_MATCHING_METHODS = [MATCHING_KNN, MATCHING_NORM_HAMMING]
136
139
  VALID_TRANSFORMS = [ALIGN_RIGID, ALIGN_HOMOGRAPHY]
137
140
  VALID_BORDER_MODES = [BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REPLICATE_BLUR]
138
- VALID_ALIGN_METHODS = [ALIGN_RANSAC, ALIGN_LMEDS]
141
+ VALID_ESTIMATION_METHODS = [ALIGN_RANSAC, ALIGN_LMEDS]
139
142
  NOKNN_METHODS = {'detectors': [DETECTOR_ORB, DETECTOR_SURF, DETECTOR_AKAZE, DETECTOR_BRISK],
140
143
  'descriptors': [DESCRIPTOR_ORB, DESCRIPTOR_AKAZE, DESCRIPTOR_BRISK]}
141
144
 
@@ -148,7 +151,7 @@ class _Constants:
148
151
  DEFAULT_ALIGN_THRESHOLD = 0.75
149
152
  DEFAULT_TRANSFORM = ALIGN_RIGID
150
153
  DEFAULT_BORDER_MODE = BORDER_REPLICATE_BLUR
151
- DEFAULT_ALIGN_METHOD = 'RANSAC'
154
+ DEFAULT_ESTIMATION_METHOD = 'RANSAC'
152
155
  DEFAULT_RANS_THRESHOLD = 3.0 # px
153
156
  DEFAULT_REFINE_ITERS = 100
154
157
  DEFAULT_ALIGN_CONFIDENCE = 99.9
@@ -160,12 +163,14 @@ class _Constants:
160
163
  DEFAULT_ALIGN_RES_TARGET_MPX = 2
161
164
  DEFAULT_ALIGN_FAST_SUBSAMPLING = False
162
165
  DEFAULT_ALIGN_MIN_GOOD_MATCHES = 20
166
+ DEFAULT_PHASE_CORR_FALLBACK = False
163
167
  ALIGN_VALID_MODES = ['auto', 'sequential', 'parallel']
164
168
  DEFAULT_ALIGN_MODE = 'auto'
165
169
  DEFAULT_ALIGN_MEMORY_LIMIT_GB = 8
166
170
  DEFAULT_ALIGN_MAX_THREADS = min(os.cpu_count() or 4, 8)
167
171
  DEFAULT_ALIGN_CHUNK_SUBMIT = True
168
172
  DEFAULT_ALIGN_BW_MATCHING = False
173
+ DEFAULT_ALIGN_DELTA_MAX = 2
169
174
 
170
175
  BALANCE_LINEAR = "LINEAR"
171
176
  BALANCE_GAMMA = "GAMMA"