shinestacker 1.3.0__py3-none-any.whl → 1.3.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.

@@ -10,7 +10,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
10
10
  import numpy as np
11
11
  from .. config.constants import constants
12
12
  from .. core.exceptions import RunStopException
13
- from .utils import read_img
13
+ from .utils import read_img, read_and_validate_img
14
14
  from .pyramid import PyramidBase
15
15
 
16
16
 
@@ -47,11 +47,11 @@ class PyramidTilesStack(PyramidBase):
47
47
  return n_steps + self.n_tiles
48
48
 
49
49
  def _process_single_image_wrapper(self, args):
50
- img_path, img_index, _n = args
51
- # self.print_message(f": processing file {img_path.split('/')[-1]}, {img_index + 1}/{n}")
52
- img = read_img(img_path)
53
- level_count = self.process_single_image(img, self.n_levels, img_index)
54
- return img_index, level_count
50
+ img_path, idx, _n = args
51
+ img = read_and_validate_img(img_path, self.shape, self.dtype)
52
+ self.check_running(self.cleanup_temp_files)
53
+ level_count = self.process_single_image(img, self.n_levels, idx)
54
+ return idx, level_count
55
55
 
56
56
  def process_single_image(self, img, levels, img_index):
57
57
  laplacian = self.single_image_laplacian(img, levels)
@@ -160,10 +160,11 @@ class PyramidTilesStack(PyramidBase):
160
160
  gc.collect()
161
161
  return np.zeros((y_end - y, x_end - x, 3), dtype=self.float_type)
162
162
 
163
- def fuse_pyramids(self, all_level_counts, num_images):
163
+ def fuse_pyramids(self, all_level_counts):
164
+ num_images = self.num_images()
164
165
  max_levels = max(all_level_counts)
165
166
  fused = []
166
- count = self._steps_per_frame * self.n_frames
167
+ count = super().total_steps(num_images)
167
168
  for level in range(max_levels - 1, -1, -1):
168
169
  self.print_message(f': fusing pyramids, layer: {level + 1}')
169
170
  if level < self.n_tiled_layers:
@@ -201,12 +202,11 @@ class PyramidTilesStack(PyramidBase):
201
202
  return fused[::-1]
202
203
 
203
204
  def focus_stack(self):
204
- n = len(self.filenames)
205
- self.focus_stack_validate(self.cleanup_temp_files)
206
- all_level_counts = [0] * n
205
+ all_level_counts = [0] * self.num_images()
207
206
  if self.num_threads > 1:
208
207
  self.print_message(f': starting parallel processing on {self.num_threads} cores')
209
- args_list = [(file_path, i, n) for i, file_path in enumerate(self.filenames)]
208
+ args_list = [(file_path, i, self.num_images())
209
+ for i, file_path in enumerate(self.filenames)]
210
210
  executor = None
211
211
  try:
212
212
  executor = ThreadPoolExecutor(max_workers=self.num_threads)
@@ -222,12 +222,11 @@ class PyramidTilesStack(PyramidBase):
222
222
  all_level_counts[img_index] = level_count
223
223
  completed_count += 1
224
224
  self.print_message(
225
- ": processing completed, image "
226
- f"{self.idx_tot_str(completed_count - 1)}")
225
+ f": processing completed, {self.image_str(completed_count - 1)}")
227
226
  except Exception as e:
228
227
  self.print_message(
229
- f"Error processing image {self.idx_tot_str(i)}: {str(e)}")
230
- self.after_step(completed_count + n + 1)
228
+ f"Error processing {self.image_str(i)}: {str(e)}")
229
+ self.after_step(completed_count)
231
230
  self.check_running(lambda: None)
232
231
  except RunStopException:
233
232
  self.print_message(": stopping image processing...")
@@ -242,16 +241,15 @@ class PyramidTilesStack(PyramidBase):
242
241
  else:
243
242
  for i, file_path in enumerate(self.filenames):
244
243
  self.print_message(
245
- f": processing file {os.path.basename(file_path)}, "
246
- f"{self.idx_tot_str(i)}")
244
+ f": processing {self.image_str(i)}")
247
245
  img = read_img(file_path)
248
246
  level_count = self.process_single_image(img, self.n_levels, i)
249
247
  all_level_counts[i] = level_count
250
- self.after_step(i + n + 1)
248
+ self.after_step(i + 1)
251
249
  self.check_running(lambda: None)
252
250
  try:
253
251
  self.check_running(lambda: None)
254
- fused_pyramid = self.fuse_pyramids(all_level_counts, n)
252
+ fused_pyramid = self.fuse_pyramids(all_level_counts)
255
253
  stacked_image = self.collapse(fused_pyramid)
256
254
  return stacked_image.astype(self.dtype)
257
255
  except RunStopException:
@@ -117,7 +117,7 @@ class ImageSequenceManager:
117
117
  assert False, "this method should be overwritten"
118
118
 
119
119
  def set_filelist(self):
120
- file_folder = self.input_full_path().replace(self.working_path, '').lstrip('/')
120
+ file_folder = os.path.relpath(self.input_full_path(), self.working_path)
121
121
  self.print_message(color_str(f"{self.num_input_filepaths()} files in folder: {file_folder}",
122
122
  constants.LOG_COLOR_LEVEL_2))
123
123
  self.base_message = color_str(self.name, constants.LOG_COLOR_LEVEL_1, "bold")
@@ -87,6 +87,17 @@ def img_bw(img):
87
87
  return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
88
88
 
89
89
 
90
+ def get_first_image_file(filenames):
91
+ first_img_file = None
92
+ for filename in filenames:
93
+ if os.path.isfile(filename) and extension_tif_jpg(filename):
94
+ first_img_file = filename
95
+ break
96
+ if first_img_file is None:
97
+ raise ValueError("No valid image files found")
98
+ return first_img_file
99
+
100
+
90
101
  def get_img_file_shape(file_path):
91
102
  img = read_img(file_path)
92
103
  return img.shape[:2]
@@ -106,6 +117,11 @@ def validate_image(img, expected_shape=None, expected_dtype=None):
106
117
  raise ShapeError(expected_shape, shape)
107
118
  if expected_dtype and dtype != expected_dtype:
108
119
  raise BitDepthError(expected_dtype, dtype)
120
+ return img
121
+
122
+
123
+ def read_and_validate_img(filename, expected_shape=None, expected_dtype=None):
124
+ return validate_image(read_img(filename), expected_shape, expected_dtype)
109
125
 
110
126
 
111
127
  def save_plot(filename):
@@ -53,3 +53,13 @@ def fill_app_menu(app, app_menu):
53
53
  exit_action.setShortcut(quit_short)
54
54
  exit_action.triggered.connect(app.quit)
55
55
  app_menu.addAction(exit_action)
56
+
57
+
58
+ def set_css_style(app):
59
+ css_style = """
60
+ QToolTip {
61
+ color: black;
62
+ border: 1px solid black;
63
+ }
64
+ """
65
+ app.setStyleSheet(css_style)
shinestacker/app/main.py CHANGED
@@ -16,7 +16,8 @@ from shinestacker.config.constants import constants
16
16
  from shinestacker.core.logging import setup_logging
17
17
  from shinestacker.gui.main_window import MainWindow
18
18
  from shinestacker.retouch.image_editor_ui import ImageEditorUI
19
- from shinestacker.app.gui_utils import disable_macos_special_menu_items, fill_app_menu
19
+ from shinestacker.app.gui_utils import (
20
+ disable_macos_special_menu_items, fill_app_menu, set_css_style)
20
21
  from shinestacker.app.help_menu import add_help_action
21
22
  from shinestacker.app.open_frames import open_frames
22
23
 
@@ -233,6 +234,7 @@ expert options are visible by default.
233
234
  app.setWindowIcon(QIcon(icon_path))
234
235
  main_app = MainApp()
235
236
  app.main_app = main_app
237
+ set_css_style(app)
236
238
  main_app.show()
237
239
  main_app.activateWindow()
238
240
  if args['expert']:
@@ -14,7 +14,8 @@ config.init(DISABLE_TQDM=True, DONT_USE_NATIVE_MENU=True)
14
14
  from shinestacker.config.constants import constants
15
15
  from shinestacker.core.logging import setup_logging
16
16
  from shinestacker.gui.main_window import MainWindow
17
- from shinestacker.app.gui_utils import disable_macos_special_menu_items, fill_app_menu
17
+ from shinestacker.app.gui_utils import (
18
+ disable_macos_special_menu_items, fill_app_menu, set_css_style)
18
19
  from shinestacker.app.help_menu import add_help_action
19
20
 
20
21
 
@@ -63,6 +64,7 @@ expert options are visible by default.
63
64
  disable_macos_special_menu_items()
64
65
  icon_path = f"{os.path.dirname(__file__)}/../gui/ico/shinestacker.png"
65
66
  app.setWindowIcon(QIcon(icon_path))
67
+ set_css_style(app)
66
68
  window = ProjectApp()
67
69
  if args['expert']:
68
70
  window.set_expert_options()
@@ -9,7 +9,8 @@ from shinestacker.config.config import config
9
9
  config.init(DISABLE_TQDM=True, DONT_USE_NATIVE_MENU=True)
10
10
  from shinestacker.config.constants import constants
11
11
  from shinestacker.retouch.image_editor_ui import ImageEditorUI
12
- from shinestacker.app.gui_utils import disable_macos_special_menu_items, fill_app_menu
12
+ from shinestacker.app.gui_utils import (
13
+ disable_macos_special_menu_items, fill_app_menu, set_css_style)
13
14
  from shinestacker.app.help_menu import add_help_action
14
15
  from shinestacker.app.open_frames import open_frames
15
16
 
@@ -60,6 +61,7 @@ Multiple directories can be specified separated by ';'.
60
61
  disable_macos_special_menu_items()
61
62
  icon_path = f"{os.path.dirname(__file__)}/../gui/ico/shinestacker.png"
62
63
  app.setWindowIcon(QIcon(icon_path))
64
+ set_css_style(app)
63
65
  editor = RetouchApp()
64
66
  app.editor = editor
65
67
  editor.show()