shinestacker 0.3.5__tar.gz → 0.4.0__tar.gz

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 (137) hide show
  1. {shinestacker-0.3.5 → shinestacker-0.4.0}/CHANGELOG.md +28 -1
  2. shinestacker-0.4.0/LICENSE +165 -0
  3. {shinestacker-0.3.5 → shinestacker-0.4.0}/PKG-INFO +8 -11
  4. {shinestacker-0.3.5 → shinestacker-0.4.0}/README.md +7 -10
  5. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/alignment.md +4 -2
  6. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/main.md +2 -5
  7. shinestacker-0.4.0/src/shinestacker/_version.py +1 -0
  8. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/align.py +37 -20
  9. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/balance.py +2 -1
  10. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/base_stack_algo.py +2 -1
  11. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/multilayer.py +11 -8
  12. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/noise_detection.py +13 -7
  13. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/pyramid.py +7 -4
  14. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/stack.py +5 -4
  15. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/stack_framework.py +12 -10
  16. shinestacker-0.4.0/src/shinestacker/app/app_config.py +22 -0
  17. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/app/main.py +1 -1
  18. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/config/config.py +22 -16
  19. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/config/constants.py +8 -1
  20. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/core/framework.py +15 -10
  21. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/action_config.py +20 -41
  22. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/actions_window.py +18 -47
  23. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/gui_logging.py +8 -7
  24. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/gui_run.py +8 -8
  25. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/main_window.py +4 -4
  26. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/new_project.py +34 -37
  27. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/project_converter.py +0 -1
  28. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/project_editor.py +43 -20
  29. shinestacker-0.4.0/src/shinestacker/gui/select_path_widget.py +32 -0
  30. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/base_filter.py +12 -1
  31. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/denoise_filter.py +4 -10
  32. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/exif_data.py +3 -9
  33. shinestacker-0.4.0/src/shinestacker/retouch/icon_container.py +19 -0
  34. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/image_editor.py +1 -1
  35. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/image_editor_ui.py +2 -1
  36. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/image_viewer.py +104 -20
  37. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/io_gui_handler.py +17 -16
  38. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/io_manager.py +0 -1
  39. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/layer_collection.py +2 -1
  40. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/shortcuts_help.py +2 -13
  41. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/unsharp_mask_filter.py +3 -10
  42. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/white_balance_filter.py +5 -13
  43. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker.egg-info/PKG-INFO +8 -11
  44. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker.egg-info/SOURCES.txt +2 -4
  45. shinestacker-0.3.5/.coverage +0 -0
  46. shinestacker-0.3.5/LICENSE +0 -1
  47. shinestacker-0.3.5/img/coins.gif +0 -0
  48. shinestacker-0.3.5/img/coins_stack.jpg +0 -0
  49. shinestacker-0.3.5/src/shinestacker/_version.py +0 -1
  50. shinestacker-0.3.5/src/shinestacker/algorithms/core_utils.py +0 -22
  51. shinestacker-0.3.5/src/shinestacker/app/app_config.py +0 -40
  52. {shinestacker-0.3.5 → shinestacker-0.4.0}/.coveragerc +0 -0
  53. {shinestacker-0.3.5 → shinestacker-0.4.0}/.flake8 +0 -0
  54. {shinestacker-0.3.5 → shinestacker-0.4.0}/.github/workflows/ci-multiplatform.yml +0 -0
  55. {shinestacker-0.3.5 → shinestacker-0.4.0}/.github/workflows/pylint.yml +0 -0
  56. {shinestacker-0.3.5 → shinestacker-0.4.0}/.github/workflows/pypi-publish.yml +0 -0
  57. {shinestacker-0.3.5 → shinestacker-0.4.0}/.github/workflows/release.yml +0 -0
  58. {shinestacker-0.3.5 → shinestacker-0.4.0}/.gitignore +0 -0
  59. {shinestacker-0.3.5 → shinestacker-0.4.0}/.pylintrc +0 -0
  60. {shinestacker-0.3.5 → shinestacker-0.4.0}/.readthedocs.yaml +0 -0
  61. {shinestacker-0.3.5 → shinestacker-0.4.0}/MANIFEST.in +0 -0
  62. {shinestacker-0.3.5 → shinestacker-0.4.0}/THIRD_PARTY_LICENSES.txt +0 -0
  63. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/api.md +0 -0
  64. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/balancing.md +0 -0
  65. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/conf.py +0 -0
  66. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/focus_stacking.md +0 -0
  67. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/gui.md +0 -0
  68. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/index.md +0 -0
  69. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/job.md +0 -0
  70. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/multilayer.md +0 -0
  71. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/noise.md +0 -0
  72. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/requirements.txt +0 -0
  73. {shinestacker-0.3.5 → shinestacker-0.4.0}/docs/vignetting.md +0 -0
  74. {shinestacker-0.3.5 → shinestacker-0.4.0}/img/coffee.gif +0 -0
  75. {shinestacker-0.3.5 → shinestacker-0.4.0}/img/coffee_stack.jpg +0 -0
  76. {shinestacker-0.3.5 → shinestacker-0.4.0}/img/extreme-vignetting.jpg +0 -0
  77. {shinestacker-0.3.5 → shinestacker-0.4.0}/img/flies.gif +0 -0
  78. {shinestacker-0.3.5 → shinestacker-0.4.0}/img/flies_stack.jpg +0 -0
  79. {shinestacker-0.3.5 → shinestacker-0.4.0}/img/flow-diagram.png +0 -0
  80. {shinestacker-0.3.5 → shinestacker-0.4.0}/img/gui-finder.png +0 -0
  81. {shinestacker-0.3.5 → shinestacker-0.4.0}/img/gui-project-new.png +0 -0
  82. {shinestacker-0.3.5 → shinestacker-0.4.0}/img/gui-project-run.png +0 -0
  83. {shinestacker-0.3.5 → shinestacker-0.4.0}/img/gui-retouch.png +0 -0
  84. {shinestacker-0.3.5 → shinestacker-0.4.0}/pyproject.toml +0 -0
  85. {shinestacker-0.3.5 → shinestacker-0.4.0}/requirements.txt +0 -0
  86. {shinestacker-0.3.5 → shinestacker-0.4.0}/scripts/build_release.py +0 -0
  87. {shinestacker-0.3.5 → shinestacker-0.4.0}/scripts/validate-tomli.py +0 -0
  88. {shinestacker-0.3.5 → shinestacker-0.4.0}/setup.cfg +0 -0
  89. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/__init__.py +0 -0
  90. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/__init__.py +0 -0
  91. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/denoise.py +0 -0
  92. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/depth_map.py +0 -0
  93. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/exif.py +0 -0
  94. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/sharpen.py +0 -0
  95. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/utils.py +0 -0
  96. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/vignetting.py +0 -0
  97. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/algorithms/white_balance.py +0 -0
  98. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/app/__init__.py +0 -0
  99. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/app/about_dialog.py +0 -0
  100. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/app/gui_utils.py +0 -0
  101. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/app/help_menu.py +0 -0
  102. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/app/open_frames.py +0 -0
  103. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/app/project.py +0 -0
  104. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/app/retouch.py +0 -0
  105. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/config/__init__.py +0 -0
  106. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/config/gui_constants.py +0 -0
  107. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/core/__init__.py +0 -0
  108. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/core/colors.py +0 -0
  109. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/core/core_utils.py +0 -0
  110. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/core/exceptions.py +0 -0
  111. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/core/logging.py +0 -0
  112. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/__init__.py +0 -0
  113. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/colors.py +0 -0
  114. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/gui_images.py +0 -0
  115. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/ico/focus_stack_bkg.png +0 -0
  116. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/ico/shinestacker.icns +0 -0
  117. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/ico/shinestacker.ico +0 -0
  118. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/ico/shinestacker.png +0 -0
  119. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/img/close-round-line-icon.png +0 -0
  120. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/img/forward-button-icon.png +0 -0
  121. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/img/play-button-round-icon.png +0 -0
  122. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/img/plus-round-line-icon.png +0 -0
  123. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/gui/project_model.py +0 -0
  124. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/__init__.py +0 -0
  125. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/brush.py +0 -0
  126. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/brush_gradient.py +0 -0
  127. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/brush_preview.py +0 -0
  128. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/brush_tool.py +0 -0
  129. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/display_manager.py +0 -0
  130. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/file_loader.py +0 -0
  131. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/filter_manager.py +0 -0
  132. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/image_filters.py +0 -0
  133. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker/retouch/undo_manager.py +0 -0
  134. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker.egg-info/dependency_links.txt +0 -0
  135. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker.egg-info/entry_points.txt +0 -0
  136. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker.egg-info/requires.txt +0 -0
  137. {shinestacker-0.3.5 → shinestacker-0.4.0}/src/shinestacker.egg-info/top_level.txt +0 -0
@@ -4,12 +4,39 @@ This page reports the main releases only and the main changes therein.
4
4
 
5
5
  ---
6
6
 
7
+ ## [v0.4.0] - 2025-08-19
8
+ **Support touchpad navigation**
9
+
10
+ ### Changes
11
+
12
+ * implemented touchpad image navigation (pan, zoom with pinch)
13
+ * alignment robustness: retry without subsampling if number of bood matches is below a threshold parameter
14
+ * added more robust path management in retouch area
15
+ * added frame count display in "New Project" dialog
16
+ * more unifrom color code in GUI run log
17
+ * code clanup, removed remnants of obsolete code
18
+ * various fixes
19
+
20
+ ---
21
+
22
+ ## [v0.3.6] - 2025-08-18
23
+ **Bug fixes**
24
+
25
+ ### Changes
26
+
27
+ * fixed a bug that prevented a complete clean up when "New Project" action is called
28
+ * fixed the management of project file path while loading and saving
29
+ * removed duplicated code
30
+ * some code clean up
31
+
32
+ ---
33
+
7
34
  ## [v0.3.5] - 2025-08-17
8
35
  **Bug fixes**
9
36
 
10
37
  ### Changes
11
38
 
12
- * fixed a bug that prevented to add sub-actoins
39
+ * fixed a bug that prevented to add sub-actions
13
40
  * vignetting constrains model parameter in order to prevent searching for dark areas at the center of the image instead of at periphery
14
41
  * updated sample images and documentation
15
42
 
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shinestacker
3
- Version: 0.3.5
3
+ Version: 0.4.0
4
4
  Summary: ShineStacker
5
5
  Author-email: Luca Lista <luka.lista@gmail.com>
6
6
  License-Expression: LGPL-3.0
@@ -37,26 +37,22 @@ Dynamic: license-file
37
37
  [![PyPI version](https://img.shields.io/pypi/v/shinestacker?color=success)](https://pypi.org/project/shinestacker/)
38
38
  [![Python Versions](https://img.shields.io/pypi/pyversions/shinestacker)](https://pypi.org/project/shinestacker/)
39
39
  [![Qt Versions](https://img.shields.io/badge/Qt-6-blue.svg?&logo=Qt&logoWidth=18&logoColor=white)](https://www.qt.io/qt-for-python)
40
- [![pylint](https://img.shields.io/badge/PyLint-9.98-yellow?logo=python&logoColor=white)](https://github.com/lucalista/shinestacker/blob/main/.github/workflows/pylint.yml)
41
- [![Documentation Status](https://readthedocs.org/projects/shinestacker/badge/?version=latest)](https://shinestacker.readthedocs.io/en/latest/?badge=latest)
42
- <!--
40
+ [![pylint](https://img.shields.io/badge/PyLint-10.00-brightgreen?logo=python&logoColor=white)](https://github.com/lucalista/shinestacker/blob/main/.github/workflows/pylint.yml)
43
41
  [![codecov](https://codecov.io/github/lucalista/shinestacker/graph/badge.svg?token=Y5NKW6VH5G)](https://codecov.io/github/lucalista/shinestacker)
44
- -->
45
-
42
+ [![Documentation Status](https://readthedocs.org/projects/shinestacker/badge/?version=latest)](https://shinestacker.readthedocs.io/en/latest/?badge=latest)
43
+ [![License: LGPL v3](https://img.shields.io/badge/License-LGPL_v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
46
44
 
47
45
  <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/flies.gif' width="400" referrerpolicy="no-referrer"> <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/flies_stack.jpg' width="400" referrerpolicy="no-referrer">
48
46
 
49
47
  <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coffee.gif' width="400" referrerpolicy="no-referrer"> <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coffee_stack.jpg' width="400" referrerpolicy="no-referrer">
50
48
 
51
- <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coins.gif' width="400" referrerpolicy="no-referrer"> <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coins_stack.jpg' width="400" referrerpolicy="no-referrer">
52
49
  > **Focus stacking** for microscopy, macro photography, and computational imaging
53
50
 
54
51
  ## Key Features
55
52
  - 🚀 **Batch Processing**: Align, balance, and stack hundreds of images
56
- - 🎨 **Hybrid Workflows**: Combine Python scripting with GUI refinement
57
53
  - 🧩 **Modular Architecture**: Mix-and-match processing modules
58
54
  - 🖌️ **Retouch Editing**: Final interactive retouch of stacked image from individual frames
59
- - 📊 **Jupyter Integration**: Reproducible research notebooks
55
+ - 📊 **Jupyter Integration**: Image processing python notebooks
60
56
 
61
57
  ## Interactive GUI
62
58
 
@@ -77,7 +73,7 @@ The GUI has two main working areas:
77
73
 
78
74
  # Credits
79
75
 
80
- The main pyramid stack algorithm was initially inspired by the [Laplacian pyramids method](https://github.com/sjawhar/focus-stacking) implementation by Sami Jawhar that was used under permission of the author for initial versions of this package. The implementation in the latest releases was rewritten from the original code.
76
+ The first version of the core focus stack algorithm was initially inspired by the [Laplacian pyramids method](https://github.com/sjawhar/focus-stacking) implementation by Sami Jawhar that was used under permission of the author. The implementation in the latest releases was rewritten from the original code.
81
77
 
82
78
  # Resources
83
79
 
@@ -87,7 +83,8 @@ Pyramid methods in image processing
87
83
 
88
84
  # License
89
85
 
90
- The software is provided as is under the [GNU Lesser General Public License v3.0](https://choosealicense.com/licenses/lgpl-3.0/).
86
+ <img src="https://www.gnu.org/graphics/lgplv3-147x51.png" alt="LGPL 3 logo">
87
+ The software is provided as is under the [GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html). See [LICENSE](https://github.com/lucalista/shinestacker/blob/main/LICENSE) for details.
91
88
 
92
89
  # Attribution request
93
90
  📸 If you publish images created with Shine Stacker, please consider adding a note such as:
@@ -6,26 +6,22 @@
6
6
  [![PyPI version](https://img.shields.io/pypi/v/shinestacker?color=success)](https://pypi.org/project/shinestacker/)
7
7
  [![Python Versions](https://img.shields.io/pypi/pyversions/shinestacker)](https://pypi.org/project/shinestacker/)
8
8
  [![Qt Versions](https://img.shields.io/badge/Qt-6-blue.svg?&logo=Qt&logoWidth=18&logoColor=white)](https://www.qt.io/qt-for-python)
9
- [![pylint](https://img.shields.io/badge/PyLint-9.98-yellow?logo=python&logoColor=white)](https://github.com/lucalista/shinestacker/blob/main/.github/workflows/pylint.yml)
10
- [![Documentation Status](https://readthedocs.org/projects/shinestacker/badge/?version=latest)](https://shinestacker.readthedocs.io/en/latest/?badge=latest)
11
- <!--
9
+ [![pylint](https://img.shields.io/badge/PyLint-10.00-brightgreen?logo=python&logoColor=white)](https://github.com/lucalista/shinestacker/blob/main/.github/workflows/pylint.yml)
12
10
  [![codecov](https://codecov.io/github/lucalista/shinestacker/graph/badge.svg?token=Y5NKW6VH5G)](https://codecov.io/github/lucalista/shinestacker)
13
- -->
14
-
11
+ [![Documentation Status](https://readthedocs.org/projects/shinestacker/badge/?version=latest)](https://shinestacker.readthedocs.io/en/latest/?badge=latest)
12
+ [![License: LGPL v3](https://img.shields.io/badge/License-LGPL_v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
15
13
 
16
14
  <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/flies.gif' width="400" referrerpolicy="no-referrer"> <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/flies_stack.jpg' width="400" referrerpolicy="no-referrer">
17
15
 
18
16
  <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coffee.gif' width="400" referrerpolicy="no-referrer"> <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coffee_stack.jpg' width="400" referrerpolicy="no-referrer">
19
17
 
20
- <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coins.gif' width="400" referrerpolicy="no-referrer"> <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coins_stack.jpg' width="400" referrerpolicy="no-referrer">
21
18
  > **Focus stacking** for microscopy, macro photography, and computational imaging
22
19
 
23
20
  ## Key Features
24
21
  - 🚀 **Batch Processing**: Align, balance, and stack hundreds of images
25
- - 🎨 **Hybrid Workflows**: Combine Python scripting with GUI refinement
26
22
  - 🧩 **Modular Architecture**: Mix-and-match processing modules
27
23
  - 🖌️ **Retouch Editing**: Final interactive retouch of stacked image from individual frames
28
- - 📊 **Jupyter Integration**: Reproducible research notebooks
24
+ - 📊 **Jupyter Integration**: Image processing python notebooks
29
25
 
30
26
  ## Interactive GUI
31
27
 
@@ -46,7 +42,7 @@ The GUI has two main working areas:
46
42
 
47
43
  # Credits
48
44
 
49
- The main pyramid stack algorithm was initially inspired by the [Laplacian pyramids method](https://github.com/sjawhar/focus-stacking) implementation by Sami Jawhar that was used under permission of the author for initial versions of this package. The implementation in the latest releases was rewritten from the original code.
45
+ The first version of the core focus stack algorithm was initially inspired by the [Laplacian pyramids method](https://github.com/sjawhar/focus-stacking) implementation by Sami Jawhar that was used under permission of the author. The implementation in the latest releases was rewritten from the original code.
50
46
 
51
47
  # Resources
52
48
 
@@ -56,7 +52,8 @@ Pyramid methods in image processing
56
52
 
57
53
  # License
58
54
 
59
- The software is provided as is under the [GNU Lesser General Public License v3.0](https://choosealicense.com/licenses/lgpl-3.0/).
55
+ <img src="https://www.gnu.org/graphics/lgplv3-147x51.png" alt="LGPL 3 logo">
56
+ The software is provided as is under the [GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html). See [LICENSE](https://github.com/lucalista/shinestacker/blob/main/LICENSE) for details.
60
57
 
61
58
  # Attribution request
62
59
  📸 If you publish images created with Shine Stacker, please consider adding a note such as:
@@ -66,7 +66,8 @@ alignment_config = {
66
66
  'border_value': (0, 0, 0, 0),
67
67
  'border_blur': 50,
68
68
  'subsample': 1,
69
- 'fast_subsampling': False
69
+ 'fast_subsampling': False,
70
+ 'min_good_matches': 100
70
71
  }
71
72
  ```
72
73
  * ```transform``` (optional, default: ```ALIGN_RIGID```): the transformation applied to register images. Possible values are:
@@ -80,7 +81,8 @@ alignment_config = {
80
81
  * ```align_confidence``` (optional, default: 99.9): alignment algorithm confidence (%). Used only if ```transform=ALIGN_RIGID```.
81
82
  * ```max_iters``` (optional, default: 2000): maximum number of iterations. Used only if ```transform=ALIGN_HOMOGRAPHY```.
82
83
  * ```subsample``` (optional, default: 1): subsample image for faster alignment. Faster, but alignment could be less accurate. It can save time, in particular for large images.
83
- * ```fast_subsampling``` (optiona, default: ```False```): perform fast image subsampling without interpolation. Used if ```subsample``` is set to ```True```.
84
+ * ```fast_subsampling``` (optional, default: ```False```): perform fast image subsampling without interpolation. Used if ```subsample``` is set to ```True```.
85
+ * ```min_good_matches``` (optional, default: 100): if ```subsample```>1 and the number of good matches is below ```min_good_matches```, the alignment is retried without subsampling. This improbes robustness in case a too large subsampling factor is specified.
84
86
  * ```border_mode``` (optional, default: ```BORDER_REPLICATE_BLUR```): border mode. See [Adding borders to your images](https://docs.opencv.org/3.4/dc/da3/tutorial_copyMakeBorder.html) for more details. Possible values are:
85
87
  * ```BORDER_CONSTANT```: pad the image with a constant value. The border value is specified with the parameter ```border_value```.
86
88
  * ```BORDER_REPLICATE```: the rows and columns at the very edge of the original are replicated to the extra border.
@@ -6,16 +6,13 @@
6
6
 
7
7
  <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coffee.gif' width="400" referrerpolicy="no-referrer"> <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coffee_stack.jpg' width="400" referrerpolicy="no-referrer">
8
8
 
9
- <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coins.gif' width="400" referrerpolicy="no-referrer"> <img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/coins_stack.jpg' width="400" referrerpolicy="no-referrer">
10
-
11
9
  > **Focus stacking** for microscopy, macro photography, and computational imaging
12
10
 
13
11
  ## Key Features
14
12
  - 🚀 **Batch Processing**: Align, balance, and stack hundreds of images
15
- - 🎨 **Hybrid Workflows**: Combine Python scripting with GUI refinement
16
13
  - 🧩 **Modular Architecture**: Mix-and-match processing modules
17
- - 🖌️ **Non-Destructive Editing**: Save multilayer TIFFs for retouching
18
- - 📊 **Jupyter Integration**: Reproducible research notebooks
14
+ - 🖌️ **Retouch Editing**: Final interactive retouch of stacked image from individual frames
15
+ - 📊 **Jupyter Integration**: Image processing python notebooks
19
16
 
20
17
 
21
18
  ## Quick start
@@ -0,0 +1 @@
1
+ __version__ = '0.4.0'
@@ -5,6 +5,7 @@ import matplotlib.pyplot as plt
5
5
  import cv2
6
6
  from .. config.constants import constants
7
7
  from .. core.exceptions import AlignmentError, InvalidOptionError
8
+ from .. core.colors import color_str
8
9
  from .utils import img_8bit, img_bw_8bit, save_plot
9
10
  from .utils import get_img_metadata, validate_image
10
11
  from .stack_framework import SubAction
@@ -33,7 +34,8 @@ _DEFAULT_ALIGNMENT_CONFIG = {
33
34
  'border_value': constants.DEFAULT_BORDER_VALUE,
34
35
  'border_blur': constants.DEFAULT_BORDER_BLUR,
35
36
  'subsample': constants.DEFAULT_ALIGN_SUBSAMPLE,
36
- 'fast_subsampling': constants.DEFAULT_ALIGN_FAST_SUBSAMPLING
37
+ 'fast_subsampling': constants.DEFAULT_ALIGN_FAST_SUBSAMPLING,
38
+ 'min_good_matches': constants.DEFAULT_ALIGN_MIN_GOOD_MATCHES
37
39
  }
38
40
 
39
41
 
@@ -164,21 +166,31 @@ def align_images(img_1, img_0, feature_config=None, matching_config=None, alignm
164
166
  if callbacks and 'message' in callbacks:
165
167
  callbacks['message']()
166
168
  subsample = alignment_config['subsample']
167
- if subsample > 1:
168
- if alignment_config['fast_subsampling']:
169
- img_0_sub, img_1_sub = img_0[::subsample, ::subsample], img_1[::subsample, ::subsample]
169
+ min_good_matches = alignment_config['min_good_matches']
170
+ while True:
171
+ if subsample > 1:
172
+ if alignment_config['fast_subsampling']:
173
+ img_0_sub, img_1_sub = \
174
+ img_0[::subsample, ::subsample], img_1[::subsample, ::subsample]
175
+ else:
176
+ img_0_sub = cv2.resize(img_0, (0, 0),
177
+ fx=1 / subsample, fy=1 / subsample,
178
+ interpolation=cv2.INTER_AREA)
179
+ img_1_sub = cv2.resize(img_1, (0, 0),
180
+ fx=1 / subsample, fy=1 / subsample,
181
+ interpolation=cv2.INTER_AREA)
170
182
  else:
171
- img_0_sub = cv2.resize(img_0, (0, 0),
172
- fx=1 / subsample, fy=1 / subsample,
173
- interpolation=cv2.INTER_AREA)
174
- img_1_sub = cv2.resize(img_1, (0, 0),
175
- fx=1 / subsample, fy=1 / subsample,
176
- interpolation=cv2.INTER_AREA)
177
- else:
178
- img_0_sub, img_1_sub = img_0, img_1
179
- kp_0, kp_1, good_matches = detect_and_compute(img_0_sub, img_1_sub,
180
- feature_config, matching_config)
181
- n_good_matches = len(good_matches)
183
+ img_0_sub, img_1_sub = img_0, img_1
184
+ kp_0, kp_1, good_matches = detect_and_compute(img_0_sub, img_1_sub,
185
+ feature_config, matching_config)
186
+ n_good_matches = len(good_matches)
187
+ if n_good_matches > min_good_matches or subsample == 1:
188
+ break
189
+ subsample = 1
190
+ if callbacks and 'warning' in callbacks:
191
+ callbacks['warning'](
192
+ f"only {n_good_matches} < {min_good_matches} matches found, "
193
+ "retrying without subsampling")
182
194
  if callbacks and 'matches_message' in callbacks:
183
195
  callbacks['matches_message'](n_good_matches)
184
196
  img_warp = None
@@ -277,14 +289,19 @@ class AlignFrames(SubAction):
277
289
  img_ref = self.process.img_ref(ref_idx)
278
290
  return self.align_images(idx, img_ref, img_0)
279
291
 
292
+ def sub_msg(self, msg, color=constants.LOG_COLOR_LEVEL_3):
293
+ self.process.sub_message_r(color_str(msg, color))
294
+
280
295
  def align_images(self, idx, img_1, img_0):
281
296
  idx_str = f"{idx:04d}"
282
297
  callbacks = {
283
- 'message': lambda: self.process.sub_message_r(': find matches'),
284
- 'matches_message': lambda n: self.process.sub_message_r(f": matches: {n}"),
285
- 'align_message': lambda: self.process.sub_message_r(': align images'),
286
- 'ecc_message': lambda: self.process.sub_message_r(": ecc refinement"),
287
- 'blur_message': lambda: self.process.sub_message_r(': blur borders'),
298
+ 'message': lambda: self.sub_msg(': find matches'),
299
+ 'matches_message': lambda n: self.sub_msg(f": good matches: {n}"),
300
+ 'align_message': lambda: self.sub_msg(': align images'),
301
+ 'ecc_message': lambda: self.sub_msg(": ecc refinement"),
302
+ 'blur_message': lambda: self.sub_msg(': blur borders'),
303
+ 'warning': lambda msg: self.sub_msg(
304
+ f': {msg}', constants.LOG_COLOR_ALERT),
288
305
  'save_plot': lambda plot_path: self.process.callback(
289
306
  'save_plot', self.process.id,
290
307
  f"{self.process.name}: matches\nframe {idx_str}", plot_path)
@@ -6,6 +6,7 @@ from scipy.optimize import bisect
6
6
  from scipy.interpolate import interp1d
7
7
  from .. config.constants import constants
8
8
  from .. core.exceptions import InvalidOptionError
9
+ from .. core.colors import color_str
9
10
  from .utils import read_img, save_plot
10
11
  from .stack_framework import SubAction
11
12
 
@@ -408,6 +409,6 @@ class BalanceFrames(SubAction):
408
409
 
409
410
  def run_frame(self, idx, _ref_idx, image):
410
411
  if idx != self.process.ref_idx:
411
- self.process.sub_message_r(': balance image')
412
+ self.process.sub_message_r(color_str(': balance image', constants.LOG_COLOR_LEVEL_3))
412
413
  image = self.correction.apply_correction(idx, image)
413
414
  return image
@@ -2,6 +2,7 @@
2
2
  import numpy as np
3
3
  from .. core.exceptions import InvalidOptionError, ImageLoadError
4
4
  from .. config.constants import constants
5
+ from .. core.colors import color_str
5
6
  from .utils import read_img, get_img_metadata, validate_image
6
7
 
7
8
 
@@ -27,7 +28,7 @@ class BaseStackAlgo:
27
28
  return self._steps_per_frame
28
29
 
29
30
  def print_message(self, msg):
30
- self.process.sub_message_r(msg)
31
+ self.process.sub_message_r(color_str(msg, constants.LOG_COLOR_LEVEL_3))
31
32
 
32
33
  def read_image_and_update_metadata(self, img_path, metadata):
33
34
  img = read_img(img_path)
@@ -176,7 +176,9 @@ class MultiLayer(JobBase, FrameMultiDirectory):
176
176
  else:
177
177
  raise RuntimeError("input_path option must contain a path or an array of paths")
178
178
  if len(paths) == 0:
179
- self.print_message(color_str("no input paths specified", "red"), level=logging.WARNING)
179
+ self.print_message(color_str("no input paths specified",
180
+ constants.LOG_COLOR_LEVEL_ALERT),
181
+ level=logging.WARNING)
180
182
  return
181
183
  files = self.folder_filelist()
182
184
  if len(files) == 0:
@@ -184,22 +186,23 @@ class MultiLayer(JobBase, FrameMultiDirectory):
184
186
  color_str(f"no input in {len(paths)} specified path" +
185
187
  ('s' if len(paths) > 1 else '') + ": "
186
188
  ", ".join([f"'{p}'" for p in paths]),
187
- "red"),
189
+ constants.LOG_COLOR_LEVEL_ALERT),
188
190
  level=logging.WARNING)
189
191
  return
190
- self.print_message(color_str("merging frames in " + self.folder_list_str(), "blue"))
192
+ self.print_message(color_str("merging frames in " + self.folder_list_str(),
193
+ constants.LOG_COLOR_LEVEL_2))
191
194
  input_files = [f"{self.working_path}/{f}" for f in files]
192
195
  self.print_message(
193
- color_str("frames: " + ", ".join([i.split("/")[-1] for i in files]), "blue"))
194
- self.print_message(
195
- color_str("reading files", "blue"))
196
+ color_str("frames: " + ", ".join([i.split("/")[-1] for i in files]),
197
+ constants.LOG_COLOR_LEVEL_2))
198
+ self.print_message(color_str("reading files", constants.LOG_COLOR_LEVEL_2))
196
199
  filename = ".".join(files[0].split("/")[-1].split(".")[:-1])
197
200
  output_file = f"{self.working_path}/{self.output_path}/{filename}.tif"
198
201
  callbacks = {
199
202
  'exif_msg': lambda path: self.print_message(
200
- color_str(f"copying exif data from path: {path}", "blue")),
203
+ color_str(f"copying exif data from path: {path}", constants.LOG_COLOR_LEVEL_2)),
201
204
  'write_msg': lambda path: self.print_message(
202
- color_str(f"writing multilayer tiff file: {path}", "blue"))
205
+ color_str(f"writing multilayer tiff file: {path}", constants.LOG_COLOR_LEVEL_2))
203
206
  }
204
207
  write_multilayer_tiff(input_files, output_file, labels=None, exif_path=self.exif_path,
205
208
  callbacks=callbacks)
@@ -73,7 +73,8 @@ class NoiseDetection(JobBase, FrameMultiDirectory):
73
73
 
74
74
  def run_core(self):
75
75
  self.print_message(color_str(
76
- f"map noisy pixels from frames in {self.folder_list_str()}", "blue"
76
+ f"map noisy pixels from frames in {self.folder_list_str()}",
77
+ constants.LOG_COLOR_LEVEL_2
77
78
  ))
78
79
  files = self.folder_filelist()
79
80
  in_paths = [self.working_path + "/" + f for f in files]
@@ -89,7 +90,7 @@ class NoiseDetection(JobBase, FrameMultiDirectory):
89
90
  mean_img = mean_image(
90
91
  file_paths=in_paths, max_frames=self.max_frames,
91
92
  message_callback=lambda path: self.print_message_r(
92
- color_str(f"reading frame: {path.split('/')[-1]}", "blue")
93
+ color_str(f"reading frame: {path.split('/')[-1]}", constants.LOG_COLOR_LEVEL_2)
93
94
  ),
94
95
  progress_callback=progress_callback)
95
96
  if not config.DISABLE_TQDM:
@@ -103,13 +104,16 @@ class NoiseDetection(JobBase, FrameMultiDirectory):
103
104
  hot_rgb = cv2.bitwise_or(hot_px[0], cv2.bitwise_or(hot_px[1], hot_px[2]))
104
105
  msg = []
105
106
  for ch, hot in zip(['rgb', *constants.RGB_LABELS], [hot_rgb] + hot_px):
106
- msg.append(f"{ch}: {np.count_nonzero(hot > 0)}")
107
- self.print_message("hot pixels: " + ", ".join(msg))
107
+ hpx = color_str(f"{ch}: {np.count_nonzero(hot > 0)}",
108
+ {'rgb': 'black', 'r': 'red', 'g': 'green', 'b': 'blue'}[ch])
109
+ msg.append(hpx)
110
+ self.print_message(color_str("hot pixels: " + ", ".join(msg), constants.LOG_COLOR_LEVEL_2))
108
111
  path = "/".join(self.file_name.split("/")[:-1])
109
112
  if not os.path.exists(f"{self.working_path}/{path}"):
110
113
  self.print_message(f"create directory: {path}")
111
114
  os.mkdir(f"{self.working_path}/{path}")
112
- self.print_message(f"writing hot pixels map file: {self.file_name}")
115
+ self.print_message(color_str(f"writing hot pixels map file: {self.file_name}",
116
+ constants.LOG_COLOR_LEVEL_2))
113
117
  cv2.imwrite(f"{self.working_path}/{self.file_name}", hot_rgb)
114
118
  plot_range = self.plot_range
115
119
  min_th, max_th = min(self.channel_thresholds), max(self.channel_thresholds)
@@ -155,7 +159,9 @@ class MaskNoise(SubAction):
155
159
  self.process = process
156
160
  path = f"{process.working_path}/{self.noise_mask}"
157
161
  if os.path.exists(path):
158
- self.process.sub_message_r(f': reading noisy pixel mask file: {self.noise_mask}')
162
+ self.process.sub_message_r(color_str(
163
+ f': reading noisy pixel mask file: {self.noise_mask}',
164
+ constants.LOG_COLOR_LEVEL_3))
159
165
  self.noise_mask_img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
160
166
  if self.noise_mask_img is None:
161
167
  raise ImageLoadError(path, f"failed to load image file {self.noise_mask}.")
@@ -163,7 +169,7 @@ class MaskNoise(SubAction):
163
169
  raise ImageLoadError(path, "file not found.")
164
170
 
165
171
  def run_frame(self, _idx, _ref_idx, image):
166
- self.process.sub_message_r(': mask noisy pixels')
172
+ self.process.sub_message_r(color_str(': mask noisy pixels', constants.LOG_COLOR_LEVEL_3))
167
173
  if len(image.shape) == 3:
168
174
  corrected = image.copy()
169
175
  for c in range(3):
@@ -12,7 +12,7 @@ class PyramidBase(BaseStackAlgo):
12
12
  kernel_size=constants.DEFAULT_PY_KERNEL_SIZE,
13
13
  gen_kernel=constants.DEFAULT_PY_GEN_KERNEL,
14
14
  float_type=constants.DEFAULT_PY_FLOAT):
15
- super().__init__("pyramid", 1, float_type)
15
+ super().__init__("pyramid", 2, float_type)
16
16
  self.min_size = min_size
17
17
  self.kernel_size = kernel_size
18
18
  self.pad_amount = (kernel_size - 1) // 2
@@ -151,11 +151,11 @@ class PyramidStack(PyramidBase):
151
151
  metadata = None
152
152
  all_laplacians = []
153
153
  levels = None
154
+ n = len(filenames)
154
155
  for i, img_path in enumerate(filenames):
155
156
  self.print_message(f": validating file {img_path.split('/')[-1]}")
156
157
 
157
158
  img, metadata, updated = self.read_image_and_update_metadata(img_path, metadata)
158
-
159
159
  if updated:
160
160
  self.dtype = metadata[1]
161
161
  self.num_pixel_values = constants.NUM_UINT8 \
@@ -163,14 +163,17 @@ class PyramidStack(PyramidBase):
163
163
  self.max_pixel_value = constants.MAX_UINT8 \
164
164
  if self.dtype == np.uint8 else constants.MAX_UINT16
165
165
  levels = int(np.log2(min(img.shape[:2]) / self.min_size))
166
-
167
166
  if self.do_step_callback:
168
167
  self.process.callback('after_step', self.process.id, self.process.name, i)
169
168
  if self.process.callback('check_running', self.process.id, self.process.name) is False:
170
169
  raise RunStopException(self.name)
171
- for img_path in filenames:
170
+ for i, img_path in enumerate(filenames):
172
171
  self.print_message(f": processing file {img_path.split('/')[-1]}")
173
172
  img = read_img(img_path)
174
173
  all_laplacians.append(self.process_single_image(img, levels))
174
+ if self.do_step_callback:
175
+ self.process.callback('after_step', self.process.id, self.process.name, i + n)
176
+ if self.process.callback('check_running', self.process.id, self.process.name) is False:
177
+ raise RunStopException(self.name)
175
178
  stacked_image = self.collapse(self.fuse_pyramids(all_laplacians))
176
179
  return stacked_image.astype(self.dtype)