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.
- shinestacker/_version.py +1 -1
- shinestacker/algorithms/__init__.py +4 -1
- shinestacker/algorithms/align.py +117 -3
- shinestacker/algorithms/balance.py +362 -163
- shinestacker/algorithms/base_stack_algo.py +6 -0
- shinestacker/algorithms/depth_map.py +1 -1
- shinestacker/algorithms/multilayer.py +12 -2
- shinestacker/algorithms/noise_detection.py +1 -1
- shinestacker/algorithms/pyramid.py +3 -2
- shinestacker/algorithms/pyramid_auto.py +141 -0
- shinestacker/algorithms/pyramid_tiles.py +199 -44
- shinestacker/algorithms/stack.py +3 -3
- shinestacker/algorithms/stack_framework.py +13 -4
- shinestacker/algorithms/utils.py +175 -1
- shinestacker/algorithms/vignetting.py +23 -5
- shinestacker/config/constants.py +29 -6
- shinestacker/gui/action_config.py +6 -7
- shinestacker/gui/action_config_dialog.py +425 -280
- shinestacker/gui/base_form_dialog.py +11 -6
- shinestacker/gui/main_window.py +3 -2
- shinestacker/gui/menu_manager.py +12 -2
- shinestacker/gui/new_project.py +27 -22
- shinestacker/gui/project_controller.py +39 -23
- shinestacker/gui/project_converter.py +2 -8
- shinestacker/gui/project_editor.py +21 -7
- 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.0.dist-info}/METADATA +1 -1
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/RECORD +34 -33
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/WHEEL +0 -0
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/entry_points.txt +0 -0
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {shinestacker-1.1.0.dist-info → shinestacker-1.2.0.dist-info}/top_level.txt +0 -0
shinestacker/algorithms/utils.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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,
|
|
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 *
|
|
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
|
-
|
|
186
|
+
subsample, self.fast_subsampling)
|
|
169
187
|
|
|
170
188
|
def begin(self, process):
|
|
171
189
|
self.process = process
|
shinestacker/config/constants.py
CHANGED
|
@@ -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,
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
32
|
+
def create_form(self, layout, action, tag="Action"):
|
|
34
33
|
pass
|
|
35
34
|
|
|
36
35
|
@abstractmethod
|
|
37
|
-
def update_params(self, params
|
|
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.
|
|
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
|
|
49
|
-
required
|
|
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.
|
|
95
|
+
add_to_layout = self.main_layout
|
|
97
96
|
add_to_layout.addRow(f"{label}:", widget)
|
|
98
97
|
return widget
|
|
99
98
|
|