shinestacker 0.5.0__py3-none-any.whl → 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of shinestacker might be problematic. Click here for more details.
- shinestacker/_version.py +1 -1
- shinestacker/algorithms/align.py +4 -12
- shinestacker/algorithms/balance.py +11 -9
- shinestacker/algorithms/depth_map.py +0 -30
- shinestacker/algorithms/utils.py +10 -0
- shinestacker/algorithms/vignetting.py +116 -70
- shinestacker/app/about_dialog.py +37 -16
- shinestacker/app/gui_utils.py +1 -1
- shinestacker/app/help_menu.py +1 -1
- shinestacker/app/main.py +2 -2
- shinestacker/app/project.py +2 -2
- shinestacker/config/constants.py +4 -1
- shinestacker/config/gui_constants.py +3 -4
- shinestacker/gui/action_config.py +5 -561
- shinestacker/gui/action_config_dialog.py +567 -0
- shinestacker/gui/base_form_dialog.py +18 -0
- shinestacker/gui/colors.py +5 -6
- shinestacker/gui/gui_logging.py +0 -1
- shinestacker/gui/gui_run.py +54 -106
- shinestacker/gui/ico/shinestacker.icns +0 -0
- shinestacker/gui/ico/shinestacker.ico +0 -0
- shinestacker/gui/ico/shinestacker.png +0 -0
- shinestacker/gui/ico/shinestacker.svg +60 -0
- shinestacker/gui/main_window.py +275 -371
- shinestacker/gui/menu_manager.py +236 -0
- shinestacker/gui/new_project.py +75 -20
- shinestacker/gui/{actions_window.py → project_controller.py} +166 -79
- shinestacker/gui/project_converter.py +6 -6
- shinestacker/gui/project_editor.py +248 -165
- shinestacker/gui/project_model.py +2 -7
- shinestacker/gui/tab_widget.py +81 -0
- shinestacker/gui/time_progress_bar.py +95 -0
- shinestacker/retouch/base_filter.py +173 -40
- shinestacker/retouch/brush_preview.py +0 -10
- shinestacker/retouch/brush_tool.py +2 -5
- shinestacker/retouch/denoise_filter.py +5 -44
- shinestacker/retouch/exif_data.py +10 -13
- shinestacker/retouch/file_loader.py +1 -1
- shinestacker/retouch/filter_manager.py +1 -4
- shinestacker/retouch/image_editor_ui.py +318 -40
- shinestacker/retouch/image_viewer.py +34 -11
- shinestacker/retouch/io_gui_handler.py +34 -30
- shinestacker/retouch/layer_collection.py +2 -0
- shinestacker/retouch/shortcuts_help.py +12 -0
- shinestacker/retouch/unsharp_mask_filter.py +10 -10
- shinestacker/retouch/vignetting_filter.py +69 -0
- shinestacker/retouch/white_balance_filter.py +46 -14
- {shinestacker-0.5.0.dist-info → shinestacker-1.0.1.dist-info}/METADATA +8 -2
- shinestacker-1.0.1.dist-info/RECORD +91 -0
- shinestacker/app/app_config.py +0 -22
- shinestacker/retouch/image_editor.py +0 -197
- shinestacker/retouch/image_filters.py +0 -69
- shinestacker-0.5.0.dist-info/RECORD +0 -87
- {shinestacker-0.5.0.dist-info → shinestacker-1.0.1.dist-info}/WHEEL +0 -0
- {shinestacker-0.5.0.dist-info → shinestacker-1.0.1.dist-info}/entry_points.txt +0 -0
- {shinestacker-0.5.0.dist-info → shinestacker-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {shinestacker-0.5.0.dist-info → shinestacker-1.0.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# pylint: disable=C0114, C0115, C0116, R0904, E0611, R0902, W0201
|
|
2
|
+
import os
|
|
3
|
+
from PySide6.QtGui import QAction, QIcon
|
|
4
|
+
from PySide6.QtWidgets import QMenu, QComboBox
|
|
5
|
+
from .. config.constants import constants
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MenuManager:
|
|
9
|
+
def __init__(self, menubar, actions, project_editor, parent):
|
|
10
|
+
self.script_dir = os.path.dirname(__file__)
|
|
11
|
+
self.project_editor = project_editor
|
|
12
|
+
self.parent = parent
|
|
13
|
+
self.menubar = menubar
|
|
14
|
+
self.actions = actions
|
|
15
|
+
self.action_selector = None
|
|
16
|
+
self.sub_action_selector = None
|
|
17
|
+
self.shortcuts = {
|
|
18
|
+
"&New...": "Ctrl+N",
|
|
19
|
+
"&Open...": "Ctrl+O",
|
|
20
|
+
"&Close": "Ctrl+W",
|
|
21
|
+
"&Save": "Ctrl+S",
|
|
22
|
+
"Save &As...": "Ctrl+Shift+S",
|
|
23
|
+
"&Undo": "Ctrl+Z",
|
|
24
|
+
"&Cut": "Ctrl+X",
|
|
25
|
+
"Cop&y": "Ctrl+C",
|
|
26
|
+
"&Paste": "Ctrl+V",
|
|
27
|
+
"Duplicate": "Ctrl+D",
|
|
28
|
+
"Delete": "Del",
|
|
29
|
+
"Move &Up": "Ctrl+Up",
|
|
30
|
+
"Move &Down": "Ctrl+Down",
|
|
31
|
+
"E&nable": "Ctrl+E",
|
|
32
|
+
"Di&sable": "Ctrl+B",
|
|
33
|
+
"Enable All": "Ctrl+Shift+E",
|
|
34
|
+
"Disable All": "Ctrl+Shift+B",
|
|
35
|
+
"Expert Options": "Ctrl+Shift+X",
|
|
36
|
+
"Add Job": "Ctrl+P",
|
|
37
|
+
"Run Job": "Ctrl+J",
|
|
38
|
+
"Run All Jobs": "Ctrl+Shift+J"
|
|
39
|
+
}
|
|
40
|
+
self.icons = {
|
|
41
|
+
"Delete": "close-round-line-icon",
|
|
42
|
+
"Add Job": "plus-round-line-icon",
|
|
43
|
+
"Run Job": "play-button-round-icon",
|
|
44
|
+
"Run All Jobs": "forward-button-icon",
|
|
45
|
+
}
|
|
46
|
+
self.tooltips = {
|
|
47
|
+
"Delete": "Delete",
|
|
48
|
+
"Add Job": "Add job",
|
|
49
|
+
"Run Job": "Run job",
|
|
50
|
+
"Run All Jobs": "Run all jobs",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
def get_icon(self, icon):
|
|
54
|
+
return QIcon(os.path.join(self.script_dir, f"img/{icon}.png"))
|
|
55
|
+
|
|
56
|
+
def action(self, name):
|
|
57
|
+
action = QAction(name, self.parent)
|
|
58
|
+
shortcut = self.shortcuts.get(name, '')
|
|
59
|
+
if shortcut:
|
|
60
|
+
action.setShortcut(shortcut)
|
|
61
|
+
icon = self.icons.get(name, '')
|
|
62
|
+
if icon:
|
|
63
|
+
action.setIcon(self.get_icon(icon))
|
|
64
|
+
tooltip = self.tooltips.get(name, '')
|
|
65
|
+
if tooltip:
|
|
66
|
+
action.setToolTip(tooltip)
|
|
67
|
+
action_fun = self.actions.get(name, None)
|
|
68
|
+
if action_fun is not None:
|
|
69
|
+
action.triggered.connect(action_fun)
|
|
70
|
+
return action
|
|
71
|
+
|
|
72
|
+
def add_file_menu(self):
|
|
73
|
+
menu = self.menubar.addMenu("&File")
|
|
74
|
+
for name in ["&New...", "&Open...", "&Close"]:
|
|
75
|
+
menu.addAction(self.action(name))
|
|
76
|
+
menu.addSeparator()
|
|
77
|
+
self.save_action = self.action("&Save")
|
|
78
|
+
menu.addAction(self.save_action)
|
|
79
|
+
self.save_as_action = self.action("Save &As...")
|
|
80
|
+
menu.addAction(self.save_as_action)
|
|
81
|
+
self.save_actions_set_enabled(False)
|
|
82
|
+
|
|
83
|
+
def add_edit_menu(self):
|
|
84
|
+
menu = self.menubar.addMenu("&Edit")
|
|
85
|
+
for name in ["&Undo", "&Cut", "Cop&y", "&Paste", "Duplicate"]:
|
|
86
|
+
menu.addAction(self.action(name))
|
|
87
|
+
self.delete_element_action = self.action("Delete")
|
|
88
|
+
self.delete_element_action.setEnabled(False)
|
|
89
|
+
menu.addAction(self.delete_element_action)
|
|
90
|
+
menu.addSeparator()
|
|
91
|
+
for name in ["Move &Up", "Move &Down"]:
|
|
92
|
+
menu.addAction(self.action(name))
|
|
93
|
+
menu.addSeparator()
|
|
94
|
+
self.enable_action = self.action("E&nable")
|
|
95
|
+
menu.addAction(self.enable_action)
|
|
96
|
+
self.disable_action = self.action("Di&sable")
|
|
97
|
+
menu.addAction(self.disable_action)
|
|
98
|
+
for name in ["Enable All", "Disable All"]:
|
|
99
|
+
menu.addAction(self.action(name))
|
|
100
|
+
|
|
101
|
+
def add_view_menu(self):
|
|
102
|
+
menu = self.menubar.addMenu("&View")
|
|
103
|
+
self.expert_options_action = self.action("Expert Options")
|
|
104
|
+
self.expert_options_action.setCheckable(True)
|
|
105
|
+
menu.addAction(self.expert_options_action)
|
|
106
|
+
|
|
107
|
+
def add_job_menu(self):
|
|
108
|
+
menu = self.menubar.addMenu("&Jobs")
|
|
109
|
+
self.add_job_action = self.action("Add Job")
|
|
110
|
+
menu.addAction(self.add_job_action)
|
|
111
|
+
menu.addSeparator()
|
|
112
|
+
self.run_job_action = self.action("Run Job")
|
|
113
|
+
self.run_job_action.setEnabled(False)
|
|
114
|
+
menu.addAction(self.run_job_action)
|
|
115
|
+
self.run_all_jobs_action = self.action("Run All Jobs")
|
|
116
|
+
self.run_all_jobs_action.setEnabled(False)
|
|
117
|
+
menu.addAction(self.run_all_jobs_action)
|
|
118
|
+
|
|
119
|
+
def add_actions_menu(self):
|
|
120
|
+
menu = self.menubar.addMenu("&Actions")
|
|
121
|
+
add_action_menu = QMenu("Add Action", self.parent)
|
|
122
|
+
for action in constants.ACTION_TYPES:
|
|
123
|
+
entry_action = QAction(action, self.parent)
|
|
124
|
+
entry_action.triggered.connect({
|
|
125
|
+
constants.ACTION_COMBO: self.add_action_combined_actions,
|
|
126
|
+
constants.ACTION_NOISEDETECTION: self.add_action_noise_detection,
|
|
127
|
+
constants.ACTION_FOCUSSTACK: self.add_action_focus_stack,
|
|
128
|
+
constants.ACTION_FOCUSSTACKBUNCH: self.add_action_focus_stack_bunch,
|
|
129
|
+
constants.ACTION_MULTILAYER: self.add_action_multilayer
|
|
130
|
+
}[action])
|
|
131
|
+
add_action_menu.addAction(entry_action)
|
|
132
|
+
menu.addMenu(add_action_menu)
|
|
133
|
+
add_sub_action_menu = QMenu("Add Sub Action", self.parent)
|
|
134
|
+
self.sub_action_menu_entries = []
|
|
135
|
+
for action in constants.SUB_ACTION_TYPES:
|
|
136
|
+
entry_action = QAction(action, self.parent)
|
|
137
|
+
entry_action.triggered.connect({
|
|
138
|
+
constants.ACTION_MASKNOISE: self.add_sub_action_make_noise,
|
|
139
|
+
constants.ACTION_VIGNETTING: self.add_sub_action_vignetting,
|
|
140
|
+
constants.ACTION_ALIGNFRAMES: self.add_sub_action_align_frames,
|
|
141
|
+
constants.ACTION_BALANCEFRAMES: self.add_sub_action_balance_frames
|
|
142
|
+
}[action])
|
|
143
|
+
entry_action.setEnabled(False)
|
|
144
|
+
self.sub_action_menu_entries.append(entry_action)
|
|
145
|
+
add_sub_action_menu.addAction(entry_action)
|
|
146
|
+
menu.addMenu(add_sub_action_menu)
|
|
147
|
+
|
|
148
|
+
def add_help_menu(self):
|
|
149
|
+
menu = self.menubar.addMenu("&Help")
|
|
150
|
+
menu.setObjectName("Help")
|
|
151
|
+
|
|
152
|
+
def add_menus(self):
|
|
153
|
+
self.add_file_menu()
|
|
154
|
+
self.add_edit_menu()
|
|
155
|
+
self.add_view_menu()
|
|
156
|
+
self.add_job_menu()
|
|
157
|
+
self.add_actions_menu()
|
|
158
|
+
self.add_help_menu()
|
|
159
|
+
|
|
160
|
+
def add_action(self, type_name=False):
|
|
161
|
+
if type_name is False:
|
|
162
|
+
type_name = self.action_selector.currentText()
|
|
163
|
+
self.project_editor.add_action(type_name)
|
|
164
|
+
|
|
165
|
+
def add_sub_action(self, type_name=False):
|
|
166
|
+
if type_name is False:
|
|
167
|
+
type_name = self.sub_action_selector.currentText()
|
|
168
|
+
self.project_editor.add_sub_action(type_name)
|
|
169
|
+
|
|
170
|
+
def save_actions_set_enabled(self, enabled):
|
|
171
|
+
self.save_action.setEnabled(enabled)
|
|
172
|
+
self.save_as_action.setEnabled(enabled)
|
|
173
|
+
|
|
174
|
+
def add_action_combined_actions(self):
|
|
175
|
+
self.add_action(constants.ACTION_COMBO)
|
|
176
|
+
|
|
177
|
+
def add_action_noise_detection(self):
|
|
178
|
+
self.add_action(constants.ACTION_NOISEDETECTION)
|
|
179
|
+
|
|
180
|
+
def add_action_focus_stack(self):
|
|
181
|
+
self.add_action(constants.ACTION_FOCUSSTACK)
|
|
182
|
+
|
|
183
|
+
def add_action_focus_stack_bunch(self):
|
|
184
|
+
self.add_action(constants.ACTION_FOCUSSTACKBUNCH)
|
|
185
|
+
|
|
186
|
+
def add_action_multilayer(self):
|
|
187
|
+
self.add_action(constants.ACTION_MULTILAYER)
|
|
188
|
+
|
|
189
|
+
def add_sub_action_make_noise(self):
|
|
190
|
+
self.add_sub_action(constants.ACTION_MASKNOISE)
|
|
191
|
+
|
|
192
|
+
def add_sub_action_vignetting(self):
|
|
193
|
+
self.add_sub_action(constants.ACTION_VIGNETTING)
|
|
194
|
+
|
|
195
|
+
def add_sub_action_align_frames(self):
|
|
196
|
+
self.add_sub_action(constants.ACTION_ALIGNFRAMES)
|
|
197
|
+
|
|
198
|
+
def add_sub_action_balance_frames(self):
|
|
199
|
+
self.add_sub_action(constants.ACTION_BALANCEFRAMES)
|
|
200
|
+
|
|
201
|
+
def fill_toolbar(self, toolbar):
|
|
202
|
+
toolbar.addAction(self.add_job_action)
|
|
203
|
+
toolbar.addSeparator()
|
|
204
|
+
self.action_selector = QComboBox()
|
|
205
|
+
self.action_selector.addItems(constants.ACTION_TYPES)
|
|
206
|
+
self.action_selector.setEnabled(False)
|
|
207
|
+
toolbar.addWidget(self.action_selector)
|
|
208
|
+
self.add_action_entry_action = QAction("Add Action", self.parent)
|
|
209
|
+
self.add_action_entry_action.setIcon(
|
|
210
|
+
QIcon(os.path.join(self.script_dir, "img/plus-round-line-icon.png")))
|
|
211
|
+
self.add_action_entry_action.setToolTip("Add action")
|
|
212
|
+
self.add_action_entry_action.triggered.connect(self.add_action)
|
|
213
|
+
self.add_action_entry_action.setEnabled(False)
|
|
214
|
+
toolbar.addAction(self.add_action_entry_action)
|
|
215
|
+
self.sub_action_selector = QComboBox()
|
|
216
|
+
self.sub_action_selector.addItems(constants.SUB_ACTION_TYPES)
|
|
217
|
+
self.sub_action_selector.setEnabled(False)
|
|
218
|
+
toolbar.addWidget(self.sub_action_selector)
|
|
219
|
+
self.add_sub_action_entry_action = QAction("Add Sub Action", self.parent)
|
|
220
|
+
self.add_sub_action_entry_action.setIcon(
|
|
221
|
+
QIcon(os.path.join(self.script_dir, "img/plus-round-line-icon.png")))
|
|
222
|
+
self.add_sub_action_entry_action.setToolTip("Add sub action")
|
|
223
|
+
self.add_sub_action_entry_action.triggered.connect(self.add_sub_action)
|
|
224
|
+
self.add_sub_action_entry_action.setEnabled(False)
|
|
225
|
+
toolbar.addAction(self.add_sub_action_entry_action)
|
|
226
|
+
toolbar.addSeparator()
|
|
227
|
+
toolbar.addAction(self.delete_element_action)
|
|
228
|
+
toolbar.addSeparator()
|
|
229
|
+
toolbar.addAction(self.run_job_action)
|
|
230
|
+
toolbar.addAction(self.run_all_jobs_action)
|
|
231
|
+
|
|
232
|
+
def set_enabled_sub_actions_gui(self, enabled):
|
|
233
|
+
self.add_sub_action_entry_action.setEnabled(enabled)
|
|
234
|
+
self.sub_action_selector.setEnabled(enabled)
|
|
235
|
+
for a in self.sub_action_menu_entries:
|
|
236
|
+
a.setEnabled(enabled)
|
shinestacker/gui/new_project.py
CHANGED
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, E0611, R0915, R0902
|
|
2
2
|
import os
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import numpy as np
|
|
4
|
+
from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLabel, QCheckBox, QSpinBox,
|
|
5
|
+
QMessageBox)
|
|
5
6
|
from PySide6.QtGui import QIcon
|
|
6
7
|
from PySide6.QtCore import Qt
|
|
7
8
|
from .. config.gui_constants import gui_constants
|
|
8
9
|
from .. config.constants import constants
|
|
10
|
+
from .. algorithms.utils import read_img
|
|
9
11
|
from .. algorithms.stack import get_bunches
|
|
10
12
|
from .select_path_widget import create_select_file_paths_widget
|
|
13
|
+
from .base_form_dialog import BaseFormDialog
|
|
11
14
|
|
|
12
15
|
DEFAULT_NO_COUNT_LABEL = " - "
|
|
16
|
+
EXTENSIONS = ['jpg', 'jpeg', 'tif', 'tiff']
|
|
13
17
|
|
|
14
18
|
|
|
15
|
-
class NewProjectDialog(
|
|
19
|
+
class NewProjectDialog(BaseFormDialog):
|
|
16
20
|
def __init__(self, parent=None):
|
|
17
|
-
super().__init__(parent)
|
|
18
|
-
self.setWindowTitle("New Project")
|
|
19
|
-
self.resize(500, self.height())
|
|
20
|
-
self.layout = QFormLayout(self)
|
|
21
|
-
self.layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
|
|
22
|
-
self.layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
|
|
23
|
-
self.layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)
|
|
24
|
-
self.layout.setLabelAlignment(Qt.AlignLeft)
|
|
21
|
+
super().__init__("New Project", parent)
|
|
25
22
|
self.create_form()
|
|
26
23
|
button_box = QHBoxLayout()
|
|
27
24
|
ok_button = QPushButton("OK")
|
|
@@ -29,9 +26,10 @@ class NewProjectDialog(QDialog):
|
|
|
29
26
|
cancel_button = QPushButton("Cancel")
|
|
30
27
|
button_box.addWidget(ok_button)
|
|
31
28
|
button_box.addWidget(cancel_button)
|
|
32
|
-
self.
|
|
29
|
+
self.add_row_to_layout(button_box)
|
|
33
30
|
ok_button.clicked.connect(self.accept)
|
|
34
31
|
cancel_button.clicked.connect(self.reject)
|
|
32
|
+
self.n_image_files = 0
|
|
35
33
|
|
|
36
34
|
def expert(self):
|
|
37
35
|
return self.parent().expert_options
|
|
@@ -41,6 +39,10 @@ class NewProjectDialog(QDialog):
|
|
|
41
39
|
label.setStyleSheet("font-weight: bold")
|
|
42
40
|
self.layout.addRow(label)
|
|
43
41
|
|
|
42
|
+
def add_label(self, label):
|
|
43
|
+
label = QLabel(label)
|
|
44
|
+
self.layout.addRow(label)
|
|
45
|
+
|
|
44
46
|
def create_form(self):
|
|
45
47
|
icon_path = f"{os.path.dirname(__file__)}/ico/shinestacker.png"
|
|
46
48
|
app_icon = QIcon(icon_path)
|
|
@@ -90,10 +92,11 @@ class NewProjectDialog(QDialog):
|
|
|
90
92
|
self.multi_layer = QCheckBox()
|
|
91
93
|
self.multi_layer.setChecked(gui_constants.NEW_PROJECT_MULTI_LAYER)
|
|
92
94
|
|
|
93
|
-
self.add_bold_label("Select input
|
|
95
|
+
self.add_bold_label("1️⃣ Select input folder, all images therein will be merged. ")
|
|
94
96
|
self.layout.addRow("Input folder:", container)
|
|
95
97
|
self.layout.addRow("Number of frames: ", self.frames_label)
|
|
96
|
-
self.
|
|
98
|
+
self.add_label("")
|
|
99
|
+
self.add_bold_label("2️⃣ Select basic options.")
|
|
97
100
|
if self.expert():
|
|
98
101
|
self.layout.addRow("Automatic noise detection:", self.noise_detection)
|
|
99
102
|
self.layout.addRow("Vignetting correction:", self.vignetting_correction)
|
|
@@ -109,6 +112,12 @@ class NewProjectDialog(QDialog):
|
|
|
109
112
|
else:
|
|
110
113
|
self.layout.addRow("Focus stack:", self.focus_stack_pyramid)
|
|
111
114
|
self.layout.addRow("Save multi layer TIFF:", self.multi_layer)
|
|
115
|
+
self.add_label("")
|
|
116
|
+
self.add_bold_label("3️⃣ Push 🆗 for further options, then press ▶️ to run.")
|
|
117
|
+
self.add_label("")
|
|
118
|
+
self.add_label("4️⃣ "
|
|
119
|
+
"Select: <b>View</b> > <b>Expert options</b> "
|
|
120
|
+
"to unlock advanced configuration.")
|
|
112
121
|
|
|
113
122
|
def update_bunch_options(self, checked):
|
|
114
123
|
self.bunch_frames.setEnabled(checked)
|
|
@@ -122,23 +131,22 @@ class NewProjectDialog(QDialog):
|
|
|
122
131
|
def count_image_files(path):
|
|
123
132
|
if path == '' or not os.path.isdir(path):
|
|
124
133
|
return 0
|
|
125
|
-
extensions = ['jpg', 'jpeg', 'tif', 'tiff']
|
|
126
134
|
count = 0
|
|
127
135
|
for filename in os.listdir(path):
|
|
128
136
|
if '.' in filename:
|
|
129
137
|
ext = filename.lower().split('.')[-1]
|
|
130
|
-
if ext in
|
|
138
|
+
if ext in EXTENSIONS:
|
|
131
139
|
count += 1
|
|
132
140
|
return count
|
|
133
141
|
|
|
134
|
-
n_image_files = count_image_files(self.input_folder.text())
|
|
135
|
-
if n_image_files == 0:
|
|
142
|
+
self.n_image_files = count_image_files(self.input_folder.text())
|
|
143
|
+
if self.n_image_files == 0:
|
|
136
144
|
self.bunches_label.setText(DEFAULT_NO_COUNT_LABEL)
|
|
137
145
|
self.frames_label.setText(DEFAULT_NO_COUNT_LABEL)
|
|
138
146
|
return
|
|
139
|
-
self.frames_label.setText(f"{n_image_files}")
|
|
147
|
+
self.frames_label.setText(f"{self.n_image_files}")
|
|
140
148
|
if self.bunch_stack.isChecked():
|
|
141
|
-
bunches = get_bunches(list(range(n_image_files)),
|
|
149
|
+
bunches = get_bunches(list(range(self.n_image_files)),
|
|
142
150
|
self.bunch_frames.value(),
|
|
143
151
|
self.bunch_overlap.value())
|
|
144
152
|
self.bunches_label.setText(f"{len(bunches)}")
|
|
@@ -159,6 +167,53 @@ class NewProjectDialog(QDialog):
|
|
|
159
167
|
if len(input_folder.split('/')) < 2:
|
|
160
168
|
QMessageBox.warning(self, "Invalid Path", "The path must have a parent folder")
|
|
161
169
|
return
|
|
170
|
+
if self.n_image_files > 0 and not self.bunch_stack.isChecked():
|
|
171
|
+
path = self.input_folder.text()
|
|
172
|
+
files = os.listdir(path)
|
|
173
|
+
file_path = None
|
|
174
|
+
for filename in files:
|
|
175
|
+
full_path = os.path.join(path, filename)
|
|
176
|
+
if os.path.isfile(full_path):
|
|
177
|
+
ext = full_path.split(".")[-1].lower()
|
|
178
|
+
if ext in EXTENSIONS:
|
|
179
|
+
file_path = full_path
|
|
180
|
+
break
|
|
181
|
+
if file_path is None:
|
|
182
|
+
QMessageBox.warning(
|
|
183
|
+
self, "Invalid input", "Could not find images now in the selected path")
|
|
184
|
+
return
|
|
185
|
+
img = read_img(file_path)
|
|
186
|
+
height, width = img.shape[:2]
|
|
187
|
+
n_bytes = 1 if img.dtype == np.uint8 else 2
|
|
188
|
+
n_bits = 8 if img.dtype == np.uint8 else 16
|
|
189
|
+
n_mbytes = float(n_bytes * height * width * self.n_image_files) / 10**6
|
|
190
|
+
if n_mbytes > 500 and not self.bunch_stack.isChecked():
|
|
191
|
+
msg = QMessageBox()
|
|
192
|
+
msg.setStyleSheet("""
|
|
193
|
+
QMessageBox {
|
|
194
|
+
min-width: 600px;
|
|
195
|
+
font-weight: bold;
|
|
196
|
+
font-size: 14px;
|
|
197
|
+
}
|
|
198
|
+
QMessageBox QLabel#qt_msgbox_informativelabel {
|
|
199
|
+
font-weight: normal;
|
|
200
|
+
font-size: 14px;
|
|
201
|
+
color: #555555;
|
|
202
|
+
}
|
|
203
|
+
""")
|
|
204
|
+
msg.setIcon(QMessageBox.Warning)
|
|
205
|
+
msg.setWindowTitle("Too many frames")
|
|
206
|
+
msg.setText(f"You selected {self.n_image_files} images "
|
|
207
|
+
f"with resolution {width}×{height} pixels, {n_bits} bits depth. "
|
|
208
|
+
"Processing may require a significant amount of memory.\n\n"
|
|
209
|
+
"Continue anyway?")
|
|
210
|
+
msg.setInformativeText("You may consider to split the processing "
|
|
211
|
+
" using a bunch stack to reduce memory usage.\n\n"
|
|
212
|
+
'✅ Check the option "Bunch stack".')
|
|
213
|
+
msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
|
|
214
|
+
msg.setDefaultButton(QMessageBox.Cancel)
|
|
215
|
+
if msg.exec_() != QMessageBox.Ok:
|
|
216
|
+
return
|
|
162
217
|
super().accept()
|
|
163
218
|
|
|
164
219
|
def get_input_folder(self):
|