shinestacker 1.6.0__py3-none-any.whl → 1.7.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 (37) hide show
  1. shinestacker/_version.py +1 -1
  2. shinestacker/algorithms/corrections.py +26 -0
  3. shinestacker/app/args_parser_opts.py +39 -0
  4. shinestacker/app/gui_utils.py +19 -2
  5. shinestacker/app/main.py +16 -25
  6. shinestacker/app/project.py +12 -21
  7. shinestacker/app/retouch.py +12 -20
  8. shinestacker/core/core_utils.py +2 -12
  9. shinestacker/core/logging.py +4 -3
  10. shinestacker/gui/ico/shinestacker.icns +0 -0
  11. shinestacker/gui/ico/shinestacker_bkg.png +0 -0
  12. shinestacker/gui/tab_widget.py +1 -5
  13. shinestacker/retouch/adjustments.py +93 -0
  14. shinestacker/retouch/base_filter.py +63 -8
  15. shinestacker/retouch/denoise_filter.py +1 -1
  16. shinestacker/retouch/display_manager.py +1 -2
  17. shinestacker/retouch/image_editor_ui.py +39 -39
  18. shinestacker/retouch/image_viewer.py +17 -9
  19. shinestacker/retouch/io_gui_handler.py +96 -44
  20. shinestacker/retouch/io_threads.py +78 -0
  21. shinestacker/retouch/layer_collection.py +12 -0
  22. shinestacker/retouch/overlaid_view.py +13 -5
  23. shinestacker/retouch/paint_area_manager.py +30 -0
  24. shinestacker/retouch/sidebyside_view.py +3 -3
  25. shinestacker/retouch/transformation_manager.py +1 -2
  26. shinestacker/retouch/undo_manager.py +15 -13
  27. shinestacker/retouch/unsharp_mask_filter.py +13 -28
  28. shinestacker/retouch/view_strategy.py +65 -22
  29. shinestacker/retouch/vignetting_filter.py +1 -1
  30. {shinestacker-1.6.0.dist-info → shinestacker-1.7.0.dist-info}/METADATA +2 -2
  31. {shinestacker-1.6.0.dist-info → shinestacker-1.7.0.dist-info}/RECORD +35 -32
  32. shinestacker/gui/ico/focus_stack_bkg.png +0 -0
  33. shinestacker/retouch/io_manager.py +0 -69
  34. {shinestacker-1.6.0.dist-info → shinestacker-1.7.0.dist-info}/WHEEL +0 -0
  35. {shinestacker-1.6.0.dist-info → shinestacker-1.7.0.dist-info}/entry_points.txt +0 -0
  36. {shinestacker-1.6.0.dist-info → shinestacker-1.7.0.dist-info}/licenses/LICENSE +0 -0
  37. {shinestacker-1.6.0.dist-info → shinestacker-1.7.0.dist-info}/top_level.txt +0 -0
@@ -16,8 +16,7 @@ class TransfromationManager(LayerCollectionHandler):
16
16
  if undoable:
17
17
  try:
18
18
  undo = self.editor.undo_manager
19
- undo.x_start, undo.x_stop = 0, 1
20
- undo.y_start, undo.y_stop = 0, 1
19
+ undo.set_paint_area(0, 1, 0, 1)
21
20
  undo.save_undo_state(self.editor.master_layer(), label)
22
21
  except Exception as e:
23
22
  traceback.print_tb(e.__traceback__)
@@ -6,13 +6,10 @@ from .. config.gui_constants import gui_constants
6
6
  class UndoManager(QObject):
7
7
  stack_changed = Signal(bool, str, bool, str)
8
8
 
9
- def __init__(self, transformation_manager):
9
+ def __init__(self, transformation_manager, paint_area_manager):
10
10
  super().__init__()
11
11
  self.transformation_manager = transformation_manager
12
- self.x_start = None
13
- self.y_start = None
14
- self.x_end = None
15
- self.y_end = None
12
+ self.paint_area_manager = paint_area_manager
16
13
  self.undo_stack = None
17
14
  self.redo_stack = None
18
15
  self.reset()
@@ -24,22 +21,25 @@ class UndoManager(QObject):
24
21
  self.stack_changed.emit(False, "", False, "")
25
22
 
26
23
  def reset_undo_area(self):
27
- self.x_end = self.y_end = 0
28
- self.x_start = self.y_start = gui_constants.MAX_UNDO_SIZE
24
+ self.paint_area_manager.reset()
29
25
 
30
26
  def extend_undo_area(self, x_start, y_start, x_end, y_end):
31
- self.x_start = min(self.x_start, x_start)
32
- self.y_start = min(self.y_start, y_start)
33
- self.x_end = max(self.x_end, x_end)
34
- self.y_end = max(self.y_end, y_end)
27
+ self.paint_area_manager.extend(x_start, y_start, x_end, y_end)
28
+
29
+ def paint_area(self):
30
+ return self.paint_area_manager.area()
31
+
32
+ def set_paint_area(self, x_start, y_start, x_end, y_end):
33
+ self.paint_area_manager.set_area(x_start, y_start, x_end, y_end)
35
34
 
36
35
  def save_undo_state(self, layer, description):
37
36
  if layer is None:
38
37
  return
39
38
  self.redo_stack = []
39
+ x_start, y_start, x_end, y_end = self.paint_area()
40
40
  undo_state = {
41
- 'master': layer[self.y_start:self.y_end, self.x_start:self.x_end].copy(),
42
- 'area': (self.x_start, self.y_start, self.x_end, self.y_end),
41
+ 'master': layer[y_start:y_end, x_start:x_end].copy(),
42
+ 'area': (x_start, y_start, x_end, y_end),
43
43
  'description': description
44
44
  }
45
45
  if len(self.undo_stack) >= gui_constants.MAX_UNDO_SIZE:
@@ -54,6 +54,7 @@ class UndoManager(QObject):
54
54
  return False
55
55
  undo_state = self.undo_stack.pop()
56
56
  x_start, y_start, x_end, y_end = undo_state['area']
57
+ self.set_paint_area(x_start, y_start, x_end, y_end)
57
58
  redo_state = {
58
59
  'master': layer[y_start:y_end, x_start:x_end].copy(),
59
60
  'area': (x_start, y_start, x_end, y_end),
@@ -80,6 +81,7 @@ class UndoManager(QObject):
80
81
  return False
81
82
  redo_state = self.redo_stack.pop()
82
83
  x_start, y_start, x_end, y_end = redo_state['area']
84
+ self.set_paint_area(x_start, y_start, x_end, y_end)
83
85
  undo_state = {
84
86
  'master': layer[y_start:y_end, x_start:x_end].copy(),
85
87
  'area': (x_start, y_start, x_end, y_end),
@@ -1,6 +1,4 @@
1
1
  # pylint: disable=C0114, C0115, C0116, E0611, W0221, R0902, R0914, R0913, R0917
2
- from PySide6.QtWidgets import QHBoxLayout, QLabel, QSlider, QDialogButtonBox
3
- from PySide6.QtCore import Qt, QTimer
4
2
  from .. algorithms.sharpen import unsharp_mask
5
3
  from .base_filter import BaseFilter
6
4
 
@@ -9,9 +7,11 @@ class UnsharpMaskFilter(BaseFilter):
9
7
  def __init__(self, name, parent, image_viewer, layer_collection, undo_manager):
10
8
  super().__init__(name, parent, image_viewer, layer_collection, undo_manager,
11
9
  preview_at_startup=True)
12
- self.max_range = 500.0
10
+ self.min_radius = 0.0
13
11
  self.max_radius = 4.0
12
+ self.min_amount = 0.0
14
13
  self.max_amount = 3.0
14
+ self.min_threshold = 0.0
15
15
  self.max_threshold = 64.0
16
16
  self.initial_radius = 1.0
17
17
  self.initial_amount = 0.5
@@ -24,31 +24,20 @@ class UnsharpMaskFilter(BaseFilter):
24
24
  dlg.setWindowTitle("Unsharp Mask")
25
25
  dlg.setMinimumWidth(600)
26
26
  params = {
27
- "Radius": (self.max_radius, self.initial_radius, "{:.2f}"),
28
- "Amount": (self.max_amount, self.initial_amount, "{:.1%}"),
29
- "Threshold": (self.max_threshold, self.initial_threshold, "{:.2f}")
27
+ "Radius": (self.min_radius, self.max_radius, self.initial_radius, "{:.2f}"),
28
+ "Amount": (self.min_amount, self.max_amount, self.initial_amount, "{:.1%}"),
29
+ "Threshold": (self.min_threshold, self.max_threshold, self.initial_threshold, "{:.2f}")
30
30
  }
31
- value_labels = {}
32
- for name, (max_val, init_val, fmt) in params.items():
33
- param_layout = QHBoxLayout()
34
- name_label = QLabel(f"{name}:")
35
- param_layout.addWidget(name_label)
36
- slider = QSlider(Qt.Horizontal)
37
- slider.setRange(0, self.max_range)
38
- slider.setValue(int(init_val / max_val * self.max_range))
39
- param_layout.addWidget(slider)
40
- value_label = QLabel(fmt.format(init_val))
41
- param_layout.addWidget(value_label)
42
- layout.addLayout(param_layout)
31
+
32
+ def set_slider(name, slider):
43
33
  if name == "Radius":
44
34
  self.radius_slider = slider
45
35
  elif name == "Amount":
46
36
  self.amount_slider = slider
47
37
  elif name == "Threshold":
48
38
  self.threshold_slider = slider
49
- value_labels[name] = value_label
50
- self.create_base_widgets(
51
- layout, QDialogButtonBox.Ok | QDialogButtonBox.Cancel, 200, dlg)
39
+
40
+ value_labels = self.create_sliders(params, dlg, layout, set_slider)
52
41
 
53
42
  def update_value(name, value, max_val, fmt):
54
43
  float_value = max_val * value / self.max_range
@@ -57,16 +46,12 @@ class UnsharpMaskFilter(BaseFilter):
57
46
  self.preview_timer.start()
58
47
 
59
48
  self.radius_slider.valueChanged.connect(
60
- lambda v: update_value("Radius", v, self.max_radius, params["Radius"][2]))
49
+ lambda v: update_value("Radius", v, self.max_radius, params["Radius"][3]))
61
50
  self.amount_slider.valueChanged.connect(
62
- lambda v: update_value("Amount", v, self.max_amount, params["Amount"][2]))
51
+ lambda v: update_value("Amount", v, self.max_amount, params["Amount"][3]))
63
52
  self.threshold_slider.valueChanged.connect(
64
53
  lambda v: update_value("Threshold", v, self.max_threshold, params["Threshold"][2]))
65
- self.preview_timer.timeout.connect(do_preview)
66
- self.connect_preview_toggle(self.preview_check, do_preview, restore_original)
67
- self.button_box.accepted.connect(dlg.accept)
68
- self.button_box.rejected.connect(dlg.reject)
69
- QTimer.singleShot(0, do_preview)
54
+ self.set_timer(do_preview, restore_original, dlg)
70
55
 
71
56
  def get_params(self):
72
57
  return (
@@ -79,10 +79,9 @@ class BrushCursor(QGraphicsItemGroup):
79
79
 
80
80
  class ViewSignals:
81
81
  temp_view_requested = Signal(bool)
82
- brush_operation_started = Signal(QPoint)
83
- brush_operation_continued = Signal(QPoint)
84
- brush_operation_ended = Signal()
82
+ end_copy_brush_area_requested = Signal()
85
83
  brush_size_change_requested = Signal(int) # +1 or -1
84
+ needs_update_requested = Signal()
86
85
 
87
86
 
88
87
  class ImageGraphicsViewBase(QGraphicsView):
@@ -103,9 +102,12 @@ class ImageGraphicsViewBase(QGraphicsView):
103
102
 
104
103
 
105
104
  class ViewStrategy(LayerCollectionHandler):
106
- def __init__(self, layer_collection, status):
105
+ def __init__(self, layer_collection, status, brush_tool, paint_area_manager):
107
106
  LayerCollectionHandler.__init__(self, layer_collection)
108
107
  self.status = status
108
+ self.brush_tool = brush_tool
109
+ self.paint_area_manager = paint_area_manager
110
+ self.mask_layer = None
109
111
  self.brush = None
110
112
  self.brush_cursor = None
111
113
  self.brush_preview = BrushPreviewItem(layer_collection)
@@ -123,6 +125,7 @@ class ViewStrategy(LayerCollectionHandler):
123
125
  self.last_update_time = QTime.currentTime()
124
126
  self.last_color_update_time = 0
125
127
  self.last_cursor_update_time = 0
128
+ self.enable_paint = True
126
129
 
127
130
  @abstractmethod
128
131
  def create_pixmaps(self):
@@ -274,6 +277,25 @@ class ViewStrategy(LayerCollectionHandler):
274
277
  view.verticalScrollBar().setValue(self.status.v_scroll)
275
278
  self.arrange_images()
276
279
 
280
+ def update_master_display_area(self):
281
+ if self.empty():
282
+ return
283
+ x_start, y_start, x_end, y_end = self.paint_area_manager.area()
284
+ dirty_region = self.master_layer()[y_start:y_end, x_start:x_end]
285
+ qimage = self.numpy_to_qimage(dirty_region)
286
+ if not qimage:
287
+ return
288
+ pixmap = QPixmap.fromImage(qimage)
289
+ master_pixmap_item = self.get_master_pixmap()
290
+ current_pixmap = master_pixmap_item.pixmap()
291
+ if current_pixmap.isNull():
292
+ self.update_master_display()
293
+ return
294
+ painter = QPainter(current_pixmap)
295
+ painter.drawPixmap(x_start, y_start, pixmap)
296
+ painter.end()
297
+ master_pixmap_item.setPixmap(current_pixmap)
298
+
277
299
  def update_master_display(self):
278
300
  self.update_view_display(
279
301
  self.master_layer(),
@@ -698,6 +720,42 @@ class ViewStrategy(LayerCollectionHandler):
698
720
  self.status.set_scroll(view.horizontalScrollBar().value(),
699
721
  view.verticalScrollBar().value())
700
722
 
723
+ def copy_brush_area_to_master(self, view_pos):
724
+ if self.layer_stack() is None or self.number_of_layers() == 0:
725
+ return
726
+ area = self.brush_tool.apply_brush_operation(
727
+ self.master_layer_copy(),
728
+ self.current_layer(),
729
+ self.master_layer(), self.mask_layer,
730
+ view_pos)
731
+ self.paint_area_manager.extend(*area)
732
+
733
+ def begin_copy_brush_area(self, pos):
734
+ self.mask_layer = self.blank_layer().copy()
735
+ self.copy_master_layer()
736
+ self.paint_area_manager.reset()
737
+ self.copy_brush_area_to_master(pos)
738
+ self.needs_update_requested.emit()
739
+
740
+ def continue_copy_brush_area(self, pos):
741
+ self.copy_brush_area_to_master(pos)
742
+ self.needs_update_requested.emit()
743
+
744
+ def mouse_press_event(self, event):
745
+ if self.empty():
746
+ return
747
+ if self.enable_paint and event.button() & Qt.LeftButton and self.has_master_layer():
748
+ if self.space_pressed:
749
+ self.scrolling = True
750
+ self.last_mouse_pos = event.position()
751
+ self.setCursor(Qt.ClosedHandCursor)
752
+ else:
753
+ self.last_brush_pos = event.position()
754
+ self.begin_copy_brush_area(event.position().toPoint())
755
+ self.dragging = True
756
+ if not self.scrolling:
757
+ self.show_brush_cursor()
758
+
701
759
  def mouse_move_event(self, event):
702
760
  if self.empty():
703
761
  return
@@ -710,7 +768,7 @@ class ViewStrategy(LayerCollectionHandler):
710
768
  brush_size = self.brush.size
711
769
  if not self.space_pressed:
712
770
  self.update_brush_cursor()
713
- if self.dragging and event.buttons() & Qt.LeftButton:
771
+ if self.enable_paint and self.dragging and event.buttons() & Qt.LeftButton:
714
772
  current_time = QTime.currentTime()
715
773
  paint_refresh_time = AppConfig.get('paint_refresh_time')
716
774
  if self.last_update_time.msecsTo(current_time) >= paint_refresh_time:
@@ -726,7 +784,7 @@ class ViewStrategy(LayerCollectionHandler):
726
784
  for i in range(0, n_steps + 1):
727
785
  pos = QPoint(self.last_brush_pos.x() + i * delta_x,
728
786
  self.last_brush_pos.y() + i * delta_y)
729
- self.brush_operation_continued.emit(pos)
787
+ self.continue_copy_brush_area(pos)
730
788
  self.last_brush_pos = position
731
789
  self.last_update_time = current_time
732
790
  if self.scrolling and event.buttons() & Qt.LeftButton:
@@ -738,21 +796,6 @@ class ViewStrategy(LayerCollectionHandler):
738
796
  self.last_mouse_pos = position
739
797
  self.scroll_view(master_view, delta.x(), delta.y())
740
798
 
741
- def mouse_press_event(self, event):
742
- if self.empty():
743
- return
744
- if event.button() == Qt.LeftButton and self.has_master_layer():
745
- if self.space_pressed:
746
- self.scrolling = True
747
- self.last_mouse_pos = event.position()
748
- self.setCursor(Qt.ClosedHandCursor)
749
- else:
750
- self.last_brush_pos = event.position()
751
- self.brush_operation_started.emit(event.position().toPoint())
752
- self.dragging = True
753
- if not self.scrolling:
754
- self.show_brush_cursor()
755
-
756
799
  def mouse_release_event(self, event):
757
800
  if self.empty():
758
801
  return
@@ -769,4 +812,4 @@ class ViewStrategy(LayerCollectionHandler):
769
812
  self.last_mouse_pos = None
770
813
  elif self.dragging:
771
814
  self.dragging = False
772
- self.brush_operation_ended.emit()
815
+ self.end_copy_brush_area_requested.emit()
@@ -9,7 +9,7 @@ from .base_filter import OneSliderBaseFilter
9
9
  class VignettingFilter(OneSliderBaseFilter):
10
10
  def __init__(self, name, parent, image_viewer, layer_collection, undo_manager):
11
11
  super().__init__(name, parent, image_viewer, layer_collection, undo_manager,
12
- 1.0, 0.90, "Vignetting correction",
12
+ 0.0, 1.0, 0.90, "Vignetting correction",
13
13
  allow_partial_preview=False, preview_at_startup=False)
14
14
  self.subsample_box = None
15
15
  self.fast_subsampling_check = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shinestacker
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: ShineStacker
5
5
  Author-email: Luca Lista <luka.lista@gmail.com>
6
6
  License-Expression: LGPL-3.0
@@ -85,7 +85,7 @@ In order to prevent this, follow the instructions below:
85
85
  1. Download the compressed archive ```shinestacker-macos.tar.gz``` in your ```Download``` folder.
86
86
  2. Double-click the archive to uncompress it. You will find a new folder ```shinestacker```.
87
87
  3. Open a terminal (*Applications > Utilities > Terminal*)
88
- 4. Type the folliwng command on the terminal:
88
+ 4. Type the folliwng command on the terminal (assuming you have expanded the ```tar.gz``` under ```Downloads```):
89
89
  ```bash
90
90
  xattr -cr ~/Downloads/shinestacker/shinestacker.app
91
91
  ```
@@ -1,11 +1,12 @@
1
1
  shinestacker/__init__.py,sha256=uq2fjAw2z_6TpH3mOcWFZ98GoEPRsNhTAK8N0MMm_e8,448
2
- shinestacker/_version.py,sha256=aWnRnF5TUBHC_2RdGczq36VyxBiPAviva2RjtMVjCFg,21
2
+ shinestacker/_version.py,sha256=ZUl2xcRi75lbPrlvl0NxpUt61vfrXEhe0YCcbPhhIX0,21
3
3
  shinestacker/algorithms/__init__.py,sha256=1FwVJ3w9GGbFFkjYJRUedTvcdE4j0ieSgaH9RC9iCY4,877
4
4
  shinestacker/algorithms/align.py,sha256=mb44u-YxZI1TTSHz81nRpX_2c8awlOhnGrK0LyfTQeQ,33543
5
5
  shinestacker/algorithms/align_auto.py,sha256=pJetw6zZEWQLouzcelkI8gD4cPiOp887ePXzVbm0E6Q,3800
6
6
  shinestacker/algorithms/align_parallel.py,sha256=5ru4HDcUObLNDP8bPUgsgwpLyflpcyVA16pPqHpYZfs,17671
7
7
  shinestacker/algorithms/balance.py,sha256=aoqnc1u5A2C3R7fKaOoKnzudRiOT8GRIu4LEP-uzyZQ,24053
8
8
  shinestacker/algorithms/base_stack_algo.py,sha256=RzxvLDHqxqhnAl83u2onjvfsRea1qGK_blbh2Vo0kxA,2670
9
+ shinestacker/algorithms/corrections.py,sha256=DrfLM33D20l4svuuBtoOiH-KGUH_BL1mAV7mHCA_nGA,1094
9
10
  shinestacker/algorithms/denoise.py,sha256=GL3Z4_6MHxSa7Wo4ZzQECZS87tHBFqO0sIVF_jPuYQU,426
10
11
  shinestacker/algorithms/depth_map.py,sha256=nRBrZQWbdUqFOtYMEQx9UNdnybrBTeAOr1eV91FlN8U,5611
11
12
  shinestacker/algorithms/exif.py,sha256=SM4ZDDe8hCJ3xY6053FNndOiwzEStzdp0WrXurlcHVc,9429
@@ -22,13 +23,13 @@ shinestacker/algorithms/vignetting.py,sha256=gJOv-FN3GnTgaVn70W_6d-qbw3WmqinDiO9
22
23
  shinestacker/algorithms/white_balance.py,sha256=PMKsBtxOSn5aRr_Gkx1StHS4eN6kBN2EhNnhg4UG24g,501
23
24
  shinestacker/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
25
  shinestacker/app/about_dialog.py,sha256=pkH7nnxUP8yc0D3vRGd1jRb5cwi1nDVbQRk_OC9yLk8,4144
25
- shinestacker/app/args_parser_opts.py,sha256=c6IUXOI0SJIFckPWPXYnwmBmdNnOcrtvU5S8hUDU_AQ,979
26
- shinestacker/app/gui_utils.py,sha256=EGZejp0XZXRLVa_Wd_2VYAwK3oe9hMSoZzNgT7NUNRw,2986
26
+ shinestacker/app/args_parser_opts.py,sha256=G3jQjxBYk87ycmyf8Idk40c5H90O1l0owz0asTodm88,2183
27
+ shinestacker/app/gui_utils.py,sha256=rNDtC6vQ1hAJ5F3Vd-VKglCE06mhleu5eiw-oitgnxU,3656
27
28
  shinestacker/app/help_menu.py,sha256=g8lKG_xZmXtNQaC3SIRzyROKVWva_PLEgZsQWh6zUcQ,499
28
- shinestacker/app/main.py,sha256=l9O9J7nICTi3wnW2uJpKfLV7HWDEi-r_xoOrbywJv_s,11129
29
+ shinestacker/app/main.py,sha256=c9_rax1eemOfkJqWV7tT7-ZkEBkqz6n4kBST8iXsjD4,10662
29
30
  shinestacker/app/open_frames.py,sha256=bsu32iJSYJQLe_tQQbvAU5DuMDVX6MRuNdE7B5lojZc,1488
30
- shinestacker/app/project.py,sha256=8hjKkZqBsumS7McHXRZdGvyHgopPHeHznrnIdXajp_w,2916
31
- shinestacker/app/retouch.py,sha256=pks7aoXmYyRoeTzOA-D2Uy5f4RKoEDh25OQtkcOE_9c,2778
31
+ shinestacker/app/project.py,sha256=nwvXllD2FBLQ4ChePQdIGVug46Wh2ubjrJ0sC7klops,2596
32
+ shinestacker/app/retouch.py,sha256=8XcYMv7-feG6yxNCpvlijZQRPlhmRK0OfZO5MuBju-0,2552
32
33
  shinestacker/app/settings_dialog.py,sha256=0P3nqqZEiTIFgidW1_e3Q_zE7NbAouNsuj-yNsU41vk,8192
33
34
  shinestacker/config/__init__.py,sha256=aXxi-LmAvXd0daIFrVnTHE5OCaYeK1uf1BKMr7oaXQs,197
34
35
  shinestacker/config/app_config.py,sha256=rM1Rndk1GDa5c0AhcVNEN9zSAzxPZixzQYfjODbJUwE,771
@@ -38,10 +39,10 @@ shinestacker/config/gui_constants.py,sha256=PNxzwmVEppJ2mV_vwp68NhWzJOEitVy1Pk9S
38
39
  shinestacker/config/settings.py,sha256=4p4r6wKOCbttzfH9tyHQSTd-iv-GfgCd1LxI3C7WIjU,3861
39
40
  shinestacker/core/__init__.py,sha256=IUEIx6SQ3DygDEHN3_E6uKpHjHtUa4a_U_1dLd_8yEU,484
40
41
  shinestacker/core/colors.py,sha256=kr_tJA1iRsdck2JaYDb2lS-codZ4Ty9gdu3kHfiWvuM,1340
41
- shinestacker/core/core_utils.py,sha256=1LYj19Dfc9jZN9-4dlf1paximDH5WZYa7DXvKr7R7QY,1719
42
+ shinestacker/core/core_utils.py,sha256=0x9iK9_iPQuj3BwF_QdWoxWTM-jyQytO57BvTQLdwmw,1378
42
43
  shinestacker/core/exceptions.py,sha256=2-noG-ORAGdvDhL8jBQFs0xxZS4fI6UIkMqrWekgk2c,1618
43
44
  shinestacker/core/framework.py,sha256=QaTfnzEUHwzlbyFG7KzeyteckTSWHWEEJE4d5Tc8H18,11015
44
- shinestacker/core/logging.py,sha256=9SuSSy9Usbh7zqmLYMqkmy-VBkOJW000lwqAR0XQs30,3067
45
+ shinestacker/core/logging.py,sha256=pN4FGcHwI5ouJKwCVoDWQx_Tg3t84mmPh0xhqszDDkw,3111
45
46
  shinestacker/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
47
  shinestacker/gui/action_config.py,sha256=Xv7SGbhPl1F_dUnU04VBt_E-wIItnN_q6QuhU_d9GfI,25929
47
48
  shinestacker/gui/action_config_dialog.py,sha256=QN95FiVPYL6uin2sYO5F7tq6G5rBWh9yRkeTVvwKrwU,38341
@@ -63,47 +64,49 @@ shinestacker/gui/project_model.py,sha256=9dId8N-np4YHDpz_wO20Mvd06np3YKlej-0TMWa
63
64
  shinestacker/gui/recent_file_manager.py,sha256=010bciuirKLiVCfOAKs0uFlB3iUjHNBlPX_9K2vH5j0,2916
64
65
  shinestacker/gui/select_path_widget.py,sha256=HSwgSr702w5Et4c-6nkRXnIpm1KFqKJetAF5xQNa5zI,1017
65
66
  shinestacker/gui/sys_mon.py,sha256=zU41YYVeqQ1-v6oGIh2_BFzQWq87keN-398Wdm59-Nk,3526
66
- shinestacker/gui/tab_widget.py,sha256=VgRmuktWXCgbXbV7c1Tho0--W5_EmmzXPfzRZgwhGfg,2965
67
+ shinestacker/gui/tab_widget.py,sha256=Hu01_neCxTOG9TMGSI-Ha1w4brp1bEXyvxS24Gi_JS8,2786
67
68
  shinestacker/gui/time_progress_bar.py,sha256=7_sllrQgayjRh__mwJ0-4lghXIakuRAx8wWucJ6olYs,3028
68
- shinestacker/gui/ico/focus_stack_bkg.png,sha256=Q86TgqvKEi_IzKI8m6aZB2a3T40UkDtexf2PdeBM9XE,163151
69
- shinestacker/gui/ico/shinestacker.icns,sha256=3IshIOv0uFexYsAEPkE9xiyuw8mB5X5gffekOUhFlt0,45278
69
+ shinestacker/gui/ico/shinestacker.icns,sha256=lKmyIUBTjpMQ6Cajcov6WA5neAbZS9-JN5ca02nCz5I,204834
70
70
  shinestacker/gui/ico/shinestacker.ico,sha256=8IMRk-toObWUz8iDXA-zHBWQ8Ps3vXN5u5ZEyw7sP3c,109613
71
71
  shinestacker/gui/ico/shinestacker.png,sha256=VybGY-nig_-wMW8g_uImGxRYvcnltWcAVEPSX6AZUHM,22448
72
72
  shinestacker/gui/ico/shinestacker.svg,sha256=r8jx5aiIT9K70MRP0ANWniFE0ctRCqH7LMQ7vcGJ8ss,6571
73
+ shinestacker/gui/ico/shinestacker_bkg.png,sha256=C91Ek4bm8hPcit4JUac8FAjRTQsHfiKIKPTZMweHKqo,10462
73
74
  shinestacker/gui/img/close-round-line-icon.png,sha256=9HZwCjgni1s_JGUPUb_MoOfoe4tRZgM5OWzk92XFZlE,8019
74
75
  shinestacker/gui/img/forward-button-icon.png,sha256=lNw86T4TOEd_uokHYF8myGSGUXzdsHvmDAjlbE18Pgo,4788
75
76
  shinestacker/gui/img/play-button-round-icon.png,sha256=9j6Ks9mOGa-2cXyRFpimepAAvSaHzqJKBfxShRb4_dE,4595
76
77
  shinestacker/gui/img/plus-round-line-icon.png,sha256=LS068Hlu-CeBvJuB3dwwdJg1lZq6D5MUIv53lu1yKJA,7534
77
78
  shinestacker/retouch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
- shinestacker/retouch/base_filter.py,sha256=zpedVvTpof8BsENWdDeu9PDul7Uw0UAXUSsyLZjbcpg,11265
79
+ shinestacker/retouch/adjustments.py,sha256=tNroGN7zjr4SsMJpB-ciSHUUOWgUWai8CChWgxZpr6Q,3826
80
+ shinestacker/retouch/base_filter.py,sha256=o_OkJbdD3jOGY--_sGL1_WqAMQI-QHGw-EEYxGhXOaQ,13976
79
81
  shinestacker/retouch/brush.py,sha256=dzD2FzSpBIPdJRmTZobcrQ1FrVd3tF__ZPnUplNE72s,357
80
82
  shinestacker/retouch/brush_gradient.py,sha256=F5SFhyzl8YTMqjJU3jK8BrIlLCYLUvITd5wz3cQE4xk,1453
81
83
  shinestacker/retouch/brush_preview.py,sha256=cOFVMCbEsgR_alzmr_-LLghtGU_unrE-hAjLHcvrZAY,5484
82
84
  shinestacker/retouch/brush_tool.py,sha256=8uVncTA375uC3Nhp2YM0eZjpOR-nN47i2eGjN8tJzOU,8714
83
- shinestacker/retouch/denoise_filter.py,sha256=UpNKbFs7uArdglEej8AUHan7oCVYV5E7HNzkovj7XMQ,571
84
- shinestacker/retouch/display_manager.py,sha256=iRPcXWb3h3OVJy-S4xny--SB-rUfU4Qc4YSVu3XstWE,10236
85
+ shinestacker/retouch/denoise_filter.py,sha256=QVXFU54MDcylNWtiIcdQSZ3eClW_xNWZhCMIeoEQ8zk,576
86
+ shinestacker/retouch/display_manager.py,sha256=fTZTGbvmX5DXagexuvbNgOF5GiH2Vv-stLUQQwoglp8,10181
85
87
  shinestacker/retouch/exif_data.py,sha256=LF-fRXW-reMq-xJ_QRE5j8DC2LVGKIlC6MR3QbC1cdg,1896
86
88
  shinestacker/retouch/file_loader.py,sha256=z02-A8_uDZxayI1NFTxT2GVUvEBWStchX9hlN1o5-0U,4784
87
89
  shinestacker/retouch/filter_manager.py,sha256=tOGIWj5HjViL1-iXHkd91X-sZ1c1G531pDmLO0x6zx0,866
88
90
  shinestacker/retouch/icon_container.py,sha256=6gw1HO1bC2FrdB4dc_iH81DQuLjzuvRGksZ2hKLT9yA,585
89
- shinestacker/retouch/image_editor_ui.py,sha256=vPfLuHdRZTGNbmX3ZXYoJ9Q2_dCDbFJHUxB41hrOD6k,34117
91
+ shinestacker/retouch/image_editor_ui.py,sha256=w6tyeYm1Arjyr-MxbLNKYvURV0qEZqigK0iUoqGy92o,34244
90
92
  shinestacker/retouch/image_view_status.py,sha256=2rWi2ugdyjMhWCtRJkwOnb7-tCtVfnGfCY_54qpZhwM,1970
91
- shinestacker/retouch/image_viewer.py,sha256=H8w-ORug1aKf7X3FeSX4lQV-a0IewZ9OVG1-50BK4cE,4452
92
- shinestacker/retouch/io_gui_handler.py,sha256=UOnrFT00s0075Ng_yJACJX9TP8UT9mQOWXwQwNAfpMw,12079
93
- shinestacker/retouch/io_manager.py,sha256=JUAA--AK0mVa1PTErJTnBFjaXIle5Qs7Ow0Wkd8at0o,2437
94
- shinestacker/retouch/layer_collection.py,sha256=fZlGrkm9-Ycc7AOzFSpImhafiTieBeCZRk-UlvlFHbo,5819
95
- shinestacker/retouch/overlaid_view.py,sha256=KBwuzC2OQsK7rdtFAKrSvXrHrEV1SiOoZhxPCQLGkvg,6734
93
+ shinestacker/retouch/image_viewer.py,sha256=xf1vYZRPb9ClCQbqrqAFhPubdqIIpku7DgcY8O5bvYU,4694
94
+ shinestacker/retouch/io_gui_handler.py,sha256=tyHMmR6_uE_IEI-CIcJibVhupxarKYHzX2fuhnesHaI,14616
95
+ shinestacker/retouch/io_threads.py,sha256=r0X4it2PfwnmiAU7eStniIfcHhPvuaqdqf5VlnvjZ-4,2832
96
+ shinestacker/retouch/layer_collection.py,sha256=xx8INSLCXIeTQn_nxfCo4QljAmQK1qukSYO1Zk4rqqo,6183
97
+ shinestacker/retouch/overlaid_view.py,sha256=QTTdegUWs99YBZZPlIRdPI5O80U3t_c3HnyegbRqNbA,7029
98
+ shinestacker/retouch/paint_area_manager.py,sha256=ilK6uQT7lzNyvdc8uNv4xTHHHAbk5hGEClJRNmiA4P8,894
96
99
  shinestacker/retouch/shortcuts_help.py,sha256=BFWTT5QvodqMhqa_9LI25hZqjICfckgyWG4fGrGzvnM,4283
97
- shinestacker/retouch/sidebyside_view.py,sha256=iwnIENV_YrR2fogXqKNdCcAQBROWkQxOnh8G4tU5bqA,18829
98
- shinestacker/retouch/transformation_manager.py,sha256=T2ewDsEKn1FBxapQ1kR7KqFwrF7spUu-KZ43bKpfBo8,1761
99
- shinestacker/retouch/undo_manager.py,sha256=_qTpwjGsznc-Sz-sfWEDv3IxJNmrYU0qi7yJelg58DU,4319
100
- shinestacker/retouch/unsharp_mask_filter.py,sha256=Iapc8UmSVpj3V0LcJq_38P5qerRqTevMynbbk5Rk6iE,3634
101
- shinestacker/retouch/view_strategy.py,sha256=l_Fuh6vmTLv7S400XAP1fDO61Dv7U8GaOrNTvjKKjvc,28521
102
- shinestacker/retouch/vignetting_filter.py,sha256=JhFr6OVIripQzSJrZEG4lxq7wBsmpofLqJQ-aP2bKw8,3789
100
+ shinestacker/retouch/sidebyside_view.py,sha256=4sNa_IUMbNH18iECO7eDO9S_ls3v2ENwP1AWrBFhofI,18907
101
+ shinestacker/retouch/transformation_manager.py,sha256=QFYCL-l9V6qlhw3y7tcs0saWWClNPsh7F9pTBkfPbRU,1711
102
+ shinestacker/retouch/undo_manager.py,sha256=J4hEAnv9bKLQ0N1wllWswjJBhgRgasCnBoMT5LEw-dM,4453
103
+ shinestacker/retouch/unsharp_mask_filter.py,sha256=SO-6ZgPPDAO9em_MMefVvvSvt01-2gm1HF6OBsShmL4,2795
104
+ shinestacker/retouch/view_strategy.py,sha256=jZxB_vX3_0notH0ClxKkLzbdtx4is3vQiYoIP-sDv3M,30216
105
+ shinestacker/retouch/vignetting_filter.py,sha256=M7PZGPdVSq4bqo6wkEznrILMIG3-mTT7iwpgK4Hieyg,3794
103
106
  shinestacker/retouch/white_balance_filter.py,sha256=UaH4yxG3fU4vPutBAkV5oTXIQyUTN09x0uTywAzv3sY,8286
104
- shinestacker-1.6.0.dist-info/licenses/LICENSE,sha256=pWgb-bBdsU2Gd2kwAXxketnm5W_2u8_fIeWEgojfrxs,7651
105
- shinestacker-1.6.0.dist-info/METADATA,sha256=0sCC4ubrLH2wabRFJaCx9_Kaj3K4HDIdM6vULEj2iJA,6978
106
- shinestacker-1.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
107
- shinestacker-1.6.0.dist-info/entry_points.txt,sha256=SY6g1LqtMmp23q1DGwLUDT_dhLX9iss8DvWkiWLyo_4,166
108
- shinestacker-1.6.0.dist-info/top_level.txt,sha256=MhijwnBVX5psfsyX8JZjqp3SYiWPsKe69f3Gnyze4Fw,13
109
- shinestacker-1.6.0.dist-info/RECORD,,
107
+ shinestacker-1.7.0.dist-info/licenses/LICENSE,sha256=pWgb-bBdsU2Gd2kwAXxketnm5W_2u8_fIeWEgojfrxs,7651
108
+ shinestacker-1.7.0.dist-info/METADATA,sha256=mAPgYAr3pS4P6hdAXRB-WBoYKXJZLRvADmX1CE0tDjA,7046
109
+ shinestacker-1.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
110
+ shinestacker-1.7.0.dist-info/entry_points.txt,sha256=SY6g1LqtMmp23q1DGwLUDT_dhLX9iss8DvWkiWLyo_4,166
111
+ shinestacker-1.7.0.dist-info/top_level.txt,sha256=MhijwnBVX5psfsyX8JZjqp3SYiWPsKe69f3Gnyze4Fw,13
112
+ shinestacker-1.7.0.dist-info/RECORD,,
Binary file
@@ -1,69 +0,0 @@
1
- # pylint: disable=E1101, C0114, C0115, C0116, E0611, W0718, R0903
2
- import traceback
3
- import cv2
4
- from PySide6.QtCore import QThread, Signal
5
- from .. algorithms.utils import read_img, validate_image, get_img_metadata
6
- from .. algorithms.exif import get_exif, write_image_with_exif_data
7
- from .. algorithms.multilayer import write_multilayer_tiff_from_images
8
- from .layer_collection import LayerCollectionHandler
9
-
10
-
11
- class FileMultilayerSaver(QThread):
12
- finished = Signal()
13
- error = Signal(str)
14
-
15
- def __init__(self, images_dict, path, exif_path=None):
16
- super().__init__()
17
- self.images_dict = images_dict
18
- self.path = path
19
- self.exif_path = exif_path
20
-
21
- def run(self):
22
- try:
23
- write_multilayer_tiff_from_images(
24
- self.images_dict, self.path, exif_path=self.exif_path)
25
- self.finished.emit()
26
- except Exception as e:
27
- traceback.print_tb(e.__traceback__)
28
- self.error.emit(str(e))
29
-
30
-
31
- class IOManager(LayerCollectionHandler):
32
- def __init__(self, layer_collection):
33
- super().__init__(layer_collection)
34
- self.exif_path = ''
35
- self.exif_data = None
36
-
37
- def import_frames(self, file_paths):
38
- stack = []
39
- labels = []
40
- master = None
41
- shape, dtype = get_img_metadata(self.master_layer())
42
- for path in file_paths:
43
- try:
44
- label = path.split("/")[-1].split(".")[0]
45
- img = cv2.cvtColor(read_img(path), cv2.COLOR_BGR2RGB)
46
- if shape is not None and dtype is not None:
47
- validate_image(img, shape, dtype)
48
- else:
49
- shape, dtype = get_img_metadata(img)
50
- label_x = label
51
- i = 0
52
- while label_x in labels:
53
- i += 1
54
- label_x = f"{label} ({i})"
55
- labels.append(label_x)
56
- stack.append(img)
57
- if master is None:
58
- master = img.copy()
59
- except Exception as e:
60
- raise RuntimeError(f"Error loading file: {path}.\n{str(e)}") from e
61
- return stack, labels, master
62
-
63
- def save_master(self, path):
64
- img = cv2.cvtColor(self.master_layer(), cv2.COLOR_RGB2BGR)
65
- write_image_with_exif_data(self.exif_data, img, path)
66
-
67
- def set_exif_data(self, path):
68
- self.exif_path = path
69
- self.exif_data = get_exif(path)