shinestacker 1.1.0__py3-none-any.whl → 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of shinestacker might be problematic. Click here for more details.

Files changed (34) hide show
  1. shinestacker/_version.py +1 -1
  2. shinestacker/algorithms/__init__.py +4 -1
  3. shinestacker/algorithms/align.py +117 -3
  4. shinestacker/algorithms/balance.py +362 -163
  5. shinestacker/algorithms/base_stack_algo.py +6 -0
  6. shinestacker/algorithms/depth_map.py +1 -1
  7. shinestacker/algorithms/multilayer.py +12 -2
  8. shinestacker/algorithms/noise_detection.py +1 -1
  9. shinestacker/algorithms/pyramid.py +3 -2
  10. shinestacker/algorithms/pyramid_auto.py +141 -0
  11. shinestacker/algorithms/pyramid_tiles.py +199 -44
  12. shinestacker/algorithms/stack.py +3 -3
  13. shinestacker/algorithms/stack_framework.py +13 -4
  14. shinestacker/algorithms/utils.py +175 -1
  15. shinestacker/algorithms/vignetting.py +23 -5
  16. shinestacker/config/constants.py +29 -6
  17. shinestacker/gui/action_config.py +6 -7
  18. shinestacker/gui/action_config_dialog.py +425 -280
  19. shinestacker/gui/base_form_dialog.py +11 -6
  20. shinestacker/gui/main_window.py +3 -2
  21. shinestacker/gui/menu_manager.py +12 -2
  22. shinestacker/gui/new_project.py +27 -22
  23. shinestacker/gui/project_controller.py +39 -23
  24. shinestacker/gui/project_converter.py +2 -8
  25. shinestacker/gui/project_editor.py +21 -7
  26. shinestacker/retouch/exif_data.py +5 -5
  27. shinestacker/retouch/shortcuts_help.py +4 -4
  28. shinestacker/retouch/vignetting_filter.py +12 -8
  29. {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/METADATA +1 -1
  30. {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/RECORD +34 -33
  31. {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/WHEEL +0 -0
  32. {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/entry_points.txt +0 -0
  33. {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/licenses/LICENSE +0 -0
  34. {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- # pylint: disable=C0114, C0116, E1101
1
+ # pylint: disable=C0114, C0116, E1101, R0914
2
2
  import os
3
3
  import logging
4
4
  import numpy as np
@@ -129,3 +129,177 @@ def img_subsample(img, subsample, fast=True):
129
129
  fx=1 / subsample, fy=1 / subsample,
130
130
  interpolation=cv2.INTER_AREA)
131
131
  return img_sub
132
+
133
+
134
+ def bgr_to_hsv(bgr_img):
135
+ if bgr_img.dtype == np.uint8:
136
+ return cv2.cvtColor(bgr_img, cv2.COLOR_BGR2HLS)
137
+ if len(bgr_img.shape) == 2:
138
+ bgr_img = cv2.merge([bgr_img, bgr_img, bgr_img])
139
+ bgr_normalized = bgr_img.astype(np.float32) / 65535.0
140
+ b, g, r = cv2.split(bgr_normalized)
141
+ v = np.max(bgr_normalized, axis=2)
142
+ m = np.min(bgr_normalized, axis=2)
143
+ delta = v - m
144
+ s = np.zeros_like(v)
145
+ nonzero_delta = delta != 0
146
+ s[nonzero_delta] = delta[nonzero_delta] / v[nonzero_delta]
147
+ h = np.zeros_like(v)
148
+ r_is_max = (v == r) & nonzero_delta
149
+ h[r_is_max] = (60 * (g[r_is_max] - b[r_is_max]) / delta[r_is_max]) % 360
150
+ g_is_max = (v == g) & nonzero_delta
151
+ h[g_is_max] = (60 * (b[g_is_max] - r[g_is_max]) / delta[g_is_max] + 120) % 360
152
+ b_is_max = (v == b) & nonzero_delta
153
+ h[b_is_max] = (60 * (r[b_is_max] - g[b_is_max]) / delta[b_is_max] + 240) % 360
154
+ h[h < 0] += 360
155
+ h_16bit = (h / 360 * 65535).astype(np.uint16)
156
+ s_16bit = (s * 65535).astype(np.uint16)
157
+ v_16bit = (v * 65535).astype(np.uint16)
158
+ return cv2.merge([h_16bit, s_16bit, v_16bit])
159
+
160
+
161
+ def hsv_to_bgr(hsv_img):
162
+ if hsv_img.dtype == np.uint8:
163
+ return cv2.cvtColor(hsv_img, cv2.COLOR_HSV2BGR)
164
+ h, s, v = cv2.split(hsv_img)
165
+ h_normalized = h.astype(np.float32) / 65535.0 * 360
166
+ s_normalized = s.astype(np.float32) / 65535.0
167
+ v_normalized = v.astype(np.float32) / 65535.0
168
+ c = v_normalized * s_normalized
169
+ x = c * (1 - np.abs((h_normalized / 60) % 2 - 1))
170
+ m = v_normalized - c
171
+ r = np.zeros_like(h, dtype=np.float32)
172
+ g = np.zeros_like(h, dtype=np.float32)
173
+ b = np.zeros_like(h, dtype=np.float32)
174
+ mask = (h_normalized >= 0) & (h_normalized < 60)
175
+ r[mask], g[mask], b[mask] = c[mask], x[mask], 0
176
+ mask = (h_normalized >= 60) & (h_normalized < 120)
177
+ r[mask], g[mask], b[mask] = x[mask], c[mask], 0
178
+ mask = (h_normalized >= 120) & (h_normalized < 180)
179
+ r[mask], g[mask], b[mask] = 0, c[mask], x[mask]
180
+ mask = (h_normalized >= 180) & (h_normalized < 240)
181
+ r[mask], g[mask], b[mask] = 0, x[mask], c[mask]
182
+ mask = (h_normalized >= 240) & (h_normalized < 300)
183
+ r[mask], g[mask], b[mask] = x[mask], 0, c[mask]
184
+ mask = (h_normalized >= 300) & (h_normalized < 360)
185
+ r[mask], g[mask], b[mask] = c[mask], 0, x[mask]
186
+ r = np.clip((r + m) * 65535, 0, 65535).astype(np.uint16)
187
+ g = np.clip((g + m) * 65535, 0, 65535).astype(np.uint16)
188
+ b = np.clip((b + m) * 65535, 0, 65535).astype(np.uint16)
189
+ return cv2.merge([b, g, r])
190
+
191
+
192
+ def bgr_to_hls(bgr_img):
193
+ if bgr_img.dtype == np.uint8:
194
+ return cv2.cvtColor(bgr_img, cv2.COLOR_BGR2HLS)
195
+ if len(bgr_img.shape) == 2:
196
+ bgr_img = cv2.merge([bgr_img, bgr_img, bgr_img])
197
+ bgr_normalized = bgr_img.astype(np.float32) / 65535.0
198
+ b, g, r = cv2.split(bgr_normalized)
199
+ max_val = np.max(bgr_normalized, axis=2)
200
+ min_val = np.min(bgr_normalized, axis=2)
201
+ delta = max_val - min_val
202
+ l = (max_val + min_val) / 2 # noqa
203
+ s = np.zeros_like(l)
204
+ mask = delta != 0
205
+ s[mask] = delta[mask] / (1 - np.abs(2 * l[mask] - 1))
206
+ h = np.zeros_like(l)
207
+ r_is_max = (max_val == r) & mask
208
+ h[r_is_max] = (60 * (g[r_is_max] - b[r_is_max]) / delta[r_is_max]) % 360
209
+ g_is_max = (max_val == g) & mask
210
+ h[g_is_max] = (60 * (b[g_is_max] - r[g_is_max]) / delta[g_is_max] + 120) % 360
211
+ b_is_max = (max_val == b) & mask
212
+ h[b_is_max] = (60 * (r[b_is_max] - g[b_is_max]) / delta[b_is_max] + 240) % 360
213
+ h[h < 0] += 360
214
+ h_16bit = (h / 360 * 65535).astype(np.uint16)
215
+ l_16bit = (l * 65535).astype(np.uint16)
216
+ s_16bit = (s * 65535).astype(np.uint16)
217
+ return cv2.merge([h_16bit, l_16bit, s_16bit])
218
+
219
+
220
+ def hls_to_bgr(hls_img):
221
+ if hls_img.dtype == np.uint8:
222
+ return cv2.cvtColor(hls_img, cv2.COLOR_HLS2BGR)
223
+ h, l, s = cv2.split(hls_img)
224
+ h_normalized = h.astype(np.float32) / 65535.0 * 360
225
+ l_normalized = l.astype(np.float32) / 65535.0
226
+ s_normalized = s.astype(np.float32) / 65535.0
227
+ c = (1 - np.abs(2 * l_normalized - 1)) * s_normalized
228
+ x = c * (1 - np.abs((h_normalized / 60) % 2 - 1))
229
+ m = l_normalized - c / 2
230
+ r = np.zeros_like(h, dtype=np.float32)
231
+ g = np.zeros_like(h, dtype=np.float32)
232
+ b = np.zeros_like(h, dtype=np.float32)
233
+ mask = (h_normalized >= 0) & (h_normalized < 60)
234
+ r[mask], g[mask], b[mask] = c[mask], x[mask], 0
235
+ mask = (h_normalized >= 60) & (h_normalized < 120)
236
+ r[mask], g[mask], b[mask] = x[mask], c[mask], 0
237
+ mask = (h_normalized >= 120) & (h_normalized < 180)
238
+ r[mask], g[mask], b[mask] = 0, c[mask], x[mask]
239
+ mask = (h_normalized >= 180) & (h_normalized < 240)
240
+ r[mask], g[mask], b[mask] = 0, x[mask], c[mask]
241
+ mask = (h_normalized >= 240) & (h_normalized < 300)
242
+ r[mask], g[mask], b[mask] = x[mask], 0, c[mask]
243
+ mask = (h_normalized >= 300) & (h_normalized < 360)
244
+ r[mask], g[mask], b[mask] = c[mask], 0, x[mask]
245
+ r = np.clip((r + m) * 65535, 0, 65535).astype(np.uint16)
246
+ g = np.clip((g + m) * 65535, 0, 65535).astype(np.uint16)
247
+ b = np.clip((b + m) * 65535, 0, 65535).astype(np.uint16)
248
+ return cv2.merge([b, g, r])
249
+
250
+
251
+ def bgr_to_lab(bgr_img):
252
+ if bgr_img.dtype == np.uint8:
253
+ return cv2.cvtColor(bgr_img, cv2.COLOR_BGR2LAB)
254
+ if len(bgr_img.shape) == 2:
255
+ bgr_img = cv2.merge([bgr_img, bgr_img, bgr_img])
256
+ bgr_normalized = bgr_img.astype(np.float32) / 65535.0
257
+ b, g, r = cv2.split(bgr_normalized)
258
+ r_linear = np.where(r > 0.04045, ((r + 0.055) / 1.055) ** 2.4, r / 12.92)
259
+ g_linear = np.where(g > 0.04045, ((g + 0.055) / 1.055) ** 2.4, g / 12.92)
260
+ b_linear = np.where(b > 0.04045, ((b + 0.055) / 1.055) ** 2.4, b / 12.92)
261
+ x = r_linear * 0.4124564 + g_linear * 0.3575761 + b_linear * 0.1804375
262
+ y = r_linear * 0.2126729 + g_linear * 0.7151522 + b_linear * 0.0721750
263
+ z = r_linear * 0.0193339 + g_linear * 0.1191920 + b_linear * 0.9503041
264
+ x /= 0.950456
265
+ z /= 1.088754
266
+ x = np.where(x > 0.008856, x ** (1 / 3), (7.787 * x) + (16 / 116))
267
+ y = np.where(y > 0.008856, y ** (1 / 3), (7.787 * y) + (16 / 116))
268
+ z = np.where(z > 0.008856, z ** (1 / 3), (7.787 * z) + (16 / 116))
269
+ l = (116 * y) - 16 # noqa
270
+ a = 500 * (x - y)
271
+ b_val = 200 * (y - z)
272
+ l_16bit = np.clip(l * 65535 / 100, 0, 65535).astype(np.uint16)
273
+ a_16bit = np.clip((a + 128) * 65535 / 255, 0, 65535).astype(np.uint16)
274
+ b_16bit = np.clip((b_val + 128) * 65535 / 255, 0, 65535).astype(np.uint16)
275
+ return cv2.merge([l_16bit, a_16bit, b_16bit])
276
+
277
+
278
+ def lab_to_bgr(lab_img):
279
+ if lab_img.dtype == np.uint8:
280
+ return cv2.cvtColor(lab_img, cv2.COLOR_LAB2BGR)
281
+ l, a, b = cv2.split(lab_img)
282
+ l_normalized = l.astype(np.float32) * 100 / 65535.0
283
+ a_normalized = a.astype(np.float32) * 255 / 65535.0 - 128
284
+ b_normalized = b.astype(np.float32) * 255 / 65535.0 - 128
285
+ y = (l_normalized + 16) / 116
286
+ x = a_normalized / 500 + y
287
+ z = y - b_normalized / 200
288
+ x = np.where(x > 0.206893, x ** 3, (x - 16 / 116) / 7.787)
289
+ y = np.where(y > 0.206893, y ** 3, (y - 16 / 116) / 7.787)
290
+ z = np.where(z > 0.206893, z ** 3, (z - 16 / 116) / 7.787)
291
+ x *= 0.950456
292
+ z *= 1.088754
293
+ r_linear = x * 3.2404542 + y * -1.5371385 + z * -0.4985314
294
+ g_linear = x * -0.9692660 + y * 1.8760108 + z * 0.0415560
295
+ b_linear = x * 0.0556434 + y * -0.2040259 + z * 1.0572252
296
+ r_linear = np.clip(r_linear, 0, 1)
297
+ g_linear = np.clip(g_linear, 0, 1)
298
+ b_linear = np.clip(b_linear, 0, 1)
299
+ r = np.where(r_linear > 0.0031308, 1.055 * (r_linear ** (1 / 2.4)) - 0.055, 12.92 * r_linear)
300
+ g = np.where(g_linear > 0.0031308, 1.055 * (g_linear ** (1 / 2.4)) - 0.055, 12.92 * g_linear)
301
+ b = np.where(b_linear > 0.0031308, 1.055 * (b_linear ** (1 / 2.4)) - 0.055, 12.92 * b_linear)
302
+ r = np.clip(r * 65535, 0, 65535).astype(np.uint16)
303
+ g = np.clip(g * 65535, 0, 65535).astype(np.uint16)
304
+ b = np.clip(b * 65535, 0, 65535).astype(np.uint16)
305
+ return cv2.merge([b, g, r])
@@ -1,4 +1,5 @@
1
1
  # pylint: disable=C0114, C0115, C0116, R0902, E1101, W0718, W0640, R0913, R0917, R0914
2
+ import math
2
3
  import traceback
3
4
  import logging
4
5
  import numpy as np
@@ -49,16 +50,30 @@ def fit_sigmoid(radii, intensities):
49
50
  return res
50
51
 
51
52
 
53
+ def subsample_factor(subsample, image):
54
+ if subsample == 0:
55
+ h, w = image.shape[:2]
56
+ img_res = (float(h) / 1000) * (float(w) / 1000)
57
+ target_res = constants.DEFAULT_BALANCE_RES_TARGET_MPX
58
+ subsample = int(1 + math.floor(img_res / target_res))
59
+ return subsample
60
+
61
+
52
62
  def img_subsampled(image, subsample=constants.DEFAULT_VIGN_SUBSAMPLE,
53
63
  fast_subsampling=constants.DEFAULT_VIGN_FAST_SUBSAMPLING):
54
64
  image_bw = cv2.cvtColor(img_8bit(image), cv2.COLOR_BGR2GRAY)
55
- return image_bw if subsample == 1 else img_subsample(image_bw, subsample, fast_subsampling)
65
+ if subsample == 0:
66
+ subsample = subsample_factor(subsample, image)
67
+ img_sub = image_bw if subsample == 1 else img_subsample(image_bw, subsample, fast_subsampling)
68
+ return img_sub
56
69
 
57
70
 
58
71
  def compute_fit_parameters(
59
72
  image, r_steps, radii=None, intensities=None,
60
73
  subsample=constants.DEFAULT_VIGN_SUBSAMPLE,
61
74
  fast_subsampling=constants.DEFAULT_VIGN_FAST_SUBSAMPLING):
75
+ if subsample == 0:
76
+ subsample = subsample_factor(subsample, image)
62
77
  image_sub = img_subsampled(image, subsample, fast_subsampling)
63
78
  if radii is None and intensities is None:
64
79
  radii, intensities = radial_mean_intensity(image_sub, r_steps)
@@ -77,6 +92,8 @@ def correct_vignetting(
77
92
  if params is None:
78
93
  if r_steps is None:
79
94
  raise RuntimeError("Either r_steps or pars must not be None")
95
+ if subsample == 0:
96
+ subsample = subsample_factor(subsample, image)
80
97
  params = compute_fit_parameters(
81
98
  image, r_steps, subsample=subsample, fast_subsampling=fast_subsampling)
82
99
  if v0 is None:
@@ -121,11 +138,12 @@ class Vignetting(SubAction):
121
138
  h, w = img_0.shape[:2]
122
139
  self.w_2, self.h_2 = w / 2, h / 2
123
140
  self.r_max = np.sqrt((w / 2)**2 + (h / 2)**2)
124
- image_sub = img_subsampled(img_0, self.subsample, self.fast_subsampling)
141
+ subsample = subsample_factor(self.subsample, img_0)
142
+ image_sub = img_subsampled(img_0, subsample, self.fast_subsampling)
125
143
  radii, intensities = radial_mean_intensity(image_sub, self.r_steps)
126
144
  try:
127
145
  params = compute_fit_parameters(
128
- img_0, self.r_steps, radii, intensities, self.subsample, self.fast_subsampling)
146
+ img_0, self.r_steps, radii, intensities, subsample, self.fast_subsampling)
129
147
  except Exception as e:
130
148
  traceback.print_tb(e.__traceback__)
131
149
  self.process.sub_message(
@@ -144,7 +162,7 @@ class Vignetting(SubAction):
144
162
  if self.plot_correction:
145
163
  plt.figure(figsize=(10, 5))
146
164
  plt.plot(radii, intensities, label="image mean intensity")
147
- plt.plot(radii, sigmoid_model(radii * self.subsample, *params), label="sigmoid fit")
165
+ plt.plot(radii, sigmoid_model(radii * subsample, *params), label="sigmoid fit")
148
166
  plt.xlabel('radius (pixels)')
149
167
  plt.ylabel('mean intensity')
150
168
  plt.legend()
@@ -165,7 +183,7 @@ class Vignetting(SubAction):
165
183
  self.process.sub_message_r(color_str(": correct vignetting", "cyan"))
166
184
  return correct_vignetting(
167
185
  img_0, self.max_correction, self.black_threshold, None, params, self.v0,
168
- self.subsample, self.fast_subsampling)
186
+ subsample, self.fast_subsampling)
169
187
 
170
188
  def begin(self, process):
171
189
  self.process = process
@@ -1,6 +1,7 @@
1
1
  # pylint: disable=C0114, C0115, C0116, C0103, R0903
2
2
  import sys
3
3
  import re
4
+ import os
4
5
 
5
6
 
6
7
  class _Constants:
@@ -12,6 +13,9 @@ class _Constants:
12
13
  NUM_UINT16 = 65536
13
14
  MAX_UINT8 = 255
14
15
  MAX_UINT16 = 65535
16
+ ONE_KILO = 1024
17
+ ONE_MEGA = ONE_KILO**2
18
+ ONE_GIGA = ONE_KILO**3
15
19
 
16
20
  LOG_FONTS = ['Monaco', 'Menlo', ' Lucida Console', 'Courier New', 'Courier', 'monospace']
17
21
  LOG_FONTS_STR = ", ".join(LOG_FONTS)
@@ -34,12 +38,17 @@ class _Constants:
34
38
  SUB_ACTION_TYPES = [ACTION_MASKNOISE, ACTION_VIGNETTING, ACTION_ALIGNFRAMES,
35
39
  ACTION_BALANCEFRAMES]
36
40
  STACK_ALGO_PYRAMID = 'Pyramid'
37
- STACK_ALGO_PYRAMID_TILES = 'Pyramid Tiles'
38
41
  STACK_ALGO_DEPTH_MAP = 'Depth map'
39
- STACK_ALGO_OPTIONS = [STACK_ALGO_PYRAMID, STACK_ALGO_PYRAMID_TILES, STACK_ALGO_DEPTH_MAP]
42
+ STACK_ALGO_OPTIONS = [STACK_ALGO_PYRAMID, STACK_ALGO_DEPTH_MAP]
40
43
  STACK_ALGO_DEFAULT = STACK_ALGO_PYRAMID
41
44
  DEFAULT_PLOTS_PATH = 'plots'
42
45
 
46
+ FIELD_SUBSAMPLE_VALUES_1 = [2, 3, 4, 6, 8, 12, 16, 24, 32]
47
+ FIELD_SUBSAMPLE_OPTIONS_1 = [f"1/{n} × 1/{n}" for n in FIELD_SUBSAMPLE_VALUES_1]
48
+ FIELD_SUBSAMPLE_VALUES = [0, 1] + FIELD_SUBSAMPLE_VALUES_1
49
+ FIELD_SUBSAMPLE_OPTIONS = ['Auto', 'Full resolution'] + FIELD_SUBSAMPLE_OPTIONS_1
50
+ FIELD_SUBSAMPLE_DEFAULT = FIELD_SUBSAMPLE_VALUES[0]
51
+
43
52
  PATH_SEPARATOR = ';'
44
53
 
45
54
  LOG_COLOR_ALERT = 'red'
@@ -51,8 +60,10 @@ class _Constants:
51
60
 
52
61
  DEFAULT_FILE_REVERSE_ORDER = False
53
62
  DEFAULT_MULTILAYER_FILE_REVERSE_ORDER = True
63
+ MULTILAYER_WARNING_MEM_GB = 1
54
64
 
55
65
  DEFAULT_NOISE_MAP_FILENAME = "noise-map/hot_pixels.png"
66
+ DEFAULT_NOISE_MAX_FRAMES = 10
56
67
  DEFAULT_MN_KERNEL_SIZE = 3
57
68
  INTERPOLATE_MEAN = 'MEAN'
58
69
  INTERPOLATE_MEDIAN = 'MEDIAN'
@@ -105,9 +116,11 @@ class _Constants:
105
116
  DEFAULT_REFINE_ITERS = 100
106
117
  DEFAULT_ALIGN_CONFIDENCE = 99.9
107
118
  DEFAULT_ALIGN_MAX_ITERS = 2000
119
+ DEFAULT_ALIGN_ABORT_ABNORMAL = True
108
120
  DEFAULT_BORDER_VALUE = [0] * 4
109
121
  DEFAULT_BORDER_BLUR = 50
110
- DEFAULT_ALIGN_SUBSAMPLE = 2
122
+ DEFAULT_ALIGN_SUBSAMPLE = 0
123
+ DEFAULT_ALIGN_RES_TARGET_MPX = 2
111
124
  DEFAULT_ALIGN_FAST_SUBSAMPLING = False
112
125
  DEFAULT_ALIGN_MIN_GOOD_MATCHES = 50
113
126
 
@@ -120,9 +133,12 @@ class _Constants:
120
133
  BALANCE_RGB = "RGB"
121
134
  BALANCE_HSV = "HSV"
122
135
  BALANCE_HLS = "HLS"
123
- VALID_BALANCE_CHANNELS = [BALANCE_LUMI, BALANCE_RGB, BALANCE_HSV, BALANCE_HLS]
136
+ BALANCE_LAB = "LAB"
137
+ VALID_BALANCE_CHANNELS = [BALANCE_LUMI, BALANCE_RGB, BALANCE_HSV, BALANCE_HLS,
138
+ BALANCE_LAB]
124
139
 
125
- DEFAULT_BALANCE_SUBSAMPLE = 8
140
+ DEFAULT_BALANCE_SUBSAMPLE = 0
141
+ DEFAULT_BALANCE_RES_TARGET_MPX = 2
126
142
  DEFAULT_BALANCE_FAST_SUBSAMPLING = False
127
143
  DEFAULT_CORR_MAP = BALANCE_LINEAR
128
144
  DEFAULT_CHANNEL = BALANCE_LUMI
@@ -131,7 +147,8 @@ class _Constants:
131
147
  DEFAULT_R_STEPS = 100
132
148
  DEFAULT_BLACK_THRESHOLD = 1.0
133
149
  DEFAULT_MAX_CORRECTION = 1
134
- DEFAULT_VIGN_SUBSAMPLE = 8
150
+ DEFAULT_VIGN_SUBSAMPLE = 0
151
+ DEFAULT_VIGN_RES_TARGET_MPX = 2
135
152
  DEFAULT_VIGN_FAST_SUBSAMPLING = False
136
153
 
137
154
  FLOAT_32 = 'float-32'
@@ -163,6 +180,12 @@ class _Constants:
163
180
  DEFAULT_PY_KERNEL_SIZE = 5
164
181
  DEFAULT_PY_GEN_KERNEL = 0.4
165
182
  DEFAULT_PY_TILE_SIZE = 512
183
+ DEFAULT_PY_N_TILED_LAYERS = 2
184
+ DEFAULT_PY_MEMORY_LIMIT_GB = 8
185
+ DEFAULT_PY_MAX_THREADS = min(os.cpu_count() or 4, 8)
186
+ DEFAULT_PY_MODE = 'auto'
187
+ PY_VALID_MODES = ['auto', 'memory', 'tiled']
188
+ MIN_PY_TILE_SIZE = 256
166
189
 
167
190
  DEFAULT_PLOT_STACK_BUNCH = False
168
191
  DEFAULT_PLOT_STACK = True
@@ -10,7 +10,6 @@ from PySide6.QtWidgets import (QPushButton, QHBoxLayout, QFileDialog, QLabel, QC
10
10
  from .. config.constants import constants
11
11
  from .select_path_widget import (create_select_file_paths_widget, create_layout_widget_no_margins,
12
12
  create_layout_widget_and_connect)
13
- from .project_model import ActionConfig
14
13
 
15
14
  FIELD_TEXT = 'text'
16
15
  FIELD_ABS_PATH = 'abs_path'
@@ -30,23 +29,23 @@ class ActionConfigurator(ABC):
30
29
  self.current_wd = current_wd
31
30
 
32
31
  @abstractmethod
33
- def create_form(self, layout, action: ActionConfig, tag="Action"):
32
+ def create_form(self, layout, action, tag="Action"):
34
33
  pass
35
34
 
36
35
  @abstractmethod
37
- def update_params(self, params: Dict[str, Any]) -> bool:
36
+ def update_params(self, params):
38
37
  pass
39
38
 
40
39
 
41
40
  class FieldBuilder:
42
41
  def __init__(self, layout, action, current_wd):
43
- self.layout = layout
42
+ self.main_layout = layout
44
43
  self.action = action
45
44
  self.current_wd = current_wd
46
45
  self.fields = {}
47
46
 
48
- def add_field(self, tag: str, field_type: str, label: str,
49
- required: bool = False, add_to_layout=None, **kwargs):
47
+ def add_field(self, tag, field_type, label,
48
+ required=False, add_to_layout=None, **kwargs):
50
49
  if field_type == FIELD_TEXT:
51
50
  widget = self.create_text_field(tag, **kwargs)
52
51
  elif field_type == FIELD_ABS_PATH:
@@ -93,7 +92,7 @@ class FieldBuilder:
93
92
  **kwargs
94
93
  }
95
94
  if add_to_layout is None:
96
- add_to_layout = self.layout
95
+ add_to_layout = self.main_layout
97
96
  add_to_layout.addRow(f"{label}:", widget)
98
97
  return widget
99
98