shinestacker 1.2.1__tar.gz → 1.3.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.
- {shinestacker-1.2.1 → shinestacker-1.3.0}/CHANGELOG.md +27 -1
- {shinestacker-1.2.1/src/shinestacker.egg-info → shinestacker-1.3.0}/PKG-INFO +6 -6
- {shinestacker-1.2.1 → shinestacker-1.3.0}/README.md +4 -5
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/alignment.md +8 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/focus_stacking.md +1 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/job.md +2 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/main.md +1 -1
- {shinestacker-1.2.1 → shinestacker-1.3.0}/pyproject.toml +1 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/requirements.txt +1 -0
- shinestacker-1.3.0/src/shinestacker/_version.py +1 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/align.py +126 -94
- shinestacker-1.3.0/src/shinestacker/algorithms/align_auto.py +64 -0
- shinestacker-1.3.0/src/shinestacker/algorithms/align_parallel.py +296 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/balance.py +3 -1
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/base_stack_algo.py +11 -2
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/multilayer.py +8 -8
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/noise_detection.py +10 -10
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/pyramid.py +4 -4
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/pyramid_auto.py +16 -10
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/pyramid_tiles.py +19 -11
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/stack.py +21 -17
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/stack_framework.py +97 -46
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/vignetting.py +13 -10
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/app/main.py +7 -3
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/config/constants.py +60 -25
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/config/gui_constants.py +1 -1
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/core/core_utils.py +4 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/core/framework.py +104 -23
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/action_config.py +4 -5
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/action_config_dialog.py +152 -12
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/base_form_dialog.py +2 -2
- shinestacker-1.3.0/src/shinestacker/gui/folder_file_selection.py +101 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/gui_run.py +12 -10
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/main_window.py +6 -1
- shinestacker-1.3.0/src/shinestacker/gui/new_project.py +348 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/project_controller.py +10 -6
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/project_converter.py +4 -2
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/project_editor.py +37 -27
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/select_path_widget.py +1 -1
- shinestacker-1.3.0/src/shinestacker/gui/sys_mon.py +96 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/time_progress_bar.py +4 -3
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/exif_data.py +1 -1
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/image_editor_ui.py +2 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0/src/shinestacker.egg-info}/PKG-INFO +6 -6
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker.egg-info/SOURCES.txt +4 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker.egg-info/requires.txt +1 -0
- shinestacker-1.2.1/src/shinestacker/_version.py +0 -1
- shinestacker-1.2.1/src/shinestacker/gui/new_project.py +0 -250
- {shinestacker-1.2.1 → shinestacker-1.3.0}/.coveragerc +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/.flake8 +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/.github/workflows/ci-multiplatform.yml +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/.github/workflows/pylint.yml +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/.github/workflows/pypi-publish.yml +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/.github/workflows/release.yml +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/.gitignore +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/.pylintrc +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/.readthedocs.yaml +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/LICENSE +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/MANIFEST.in +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/THIRD_PARTY_LICENSES.txt +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/api.md +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/balancing.md +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/conf.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/gui.md +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/index.md +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/multilayer.md +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/noise.md +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/requirements.txt +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/docs/vignetting.md +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/img/coffee.gif +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/img/coffee_stack.jpg +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/img/extreme-vignetting.jpg +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/img/flies.gif +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/img/flies_stack.jpg +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/img/flow-diagram.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/img/gui-finder.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/img/gui-project-new.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/img/gui-project-run.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/img/gui-retouch.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/scripts/build_release.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/scripts/git-rev-list.sh +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/scripts/validate-tomli.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/setup.cfg +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/__init__.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/__init__.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/denoise.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/depth_map.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/exif.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/sharpen.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/utils.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/algorithms/white_balance.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/app/__init__.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/app/about_dialog.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/app/gui_utils.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/app/help_menu.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/app/open_frames.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/app/project.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/app/retouch.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/config/__init__.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/config/config.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/core/__init__.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/core/colors.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/core/exceptions.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/core/logging.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/__init__.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/colors.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/flow_layout.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/gui_images.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/gui_logging.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/ico/focus_stack_bkg.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/ico/shinestacker.icns +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/ico/shinestacker.ico +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/ico/shinestacker.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/ico/shinestacker.svg +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/img/close-round-line-icon.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/img/forward-button-icon.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/img/play-button-round-icon.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/img/plus-round-line-icon.png +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/menu_manager.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/project_model.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/gui/tab_widget.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/__init__.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/base_filter.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/brush.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/brush_gradient.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/brush_preview.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/brush_tool.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/denoise_filter.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/display_manager.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/file_loader.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/filter_manager.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/icon_container.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/image_viewer.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/io_gui_handler.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/io_manager.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/layer_collection.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/shortcuts_help.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/undo_manager.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/unsharp_mask_filter.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/vignetting_filter.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker/retouch/white_balance_filter.py +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker.egg-info/dependency_links.txt +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker.egg-info/entry_points.txt +0 -0
- {shinestacker-1.2.1 → shinestacker-1.3.0}/src/shinestacker.egg-info/top_level.txt +0 -0
|
@@ -2,8 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
This page reports the main releases only and the main changes therein.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
## [v1.3.0] - 2025-09-05
|
|
7
|
+
**Parallel processing and input flexibility**
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Parallel processing in alignment feature extraction
|
|
11
|
+
- Parallel processing of combined actions
|
|
12
|
+
- Job input can now specify a list of files (not only a folder)
|
|
13
|
+
- CPU and memory usage monitor widget for running jobs
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Path in example project
|
|
17
|
+
- Bug fix in config dialog
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Changes some default parameters for better performances
|
|
21
|
+
- Some GUI restyling
|
|
22
|
+
- Code cleanup
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## [v1.2.1] - 2025-09-02
|
|
6
27
|
**Bug fixes and minor improvements**
|
|
28
|
+
|
|
29
|
+
### Changes
|
|
30
|
+
|
|
7
31
|
* alignment is more tolerant in case of failures: frames are skipped and the running job is not stopped
|
|
8
32
|
* fixed the -x (--expert) option
|
|
9
33
|
* more safety checks prevent crashes for abnormal conditions
|
|
@@ -16,6 +40,8 @@ This page reports the main releases only and the main changes therein.
|
|
|
16
40
|
## [v1.2.0] - 2025-08-31
|
|
17
41
|
**Parallel processing and more improvements**
|
|
18
42
|
|
|
43
|
+
### Changes
|
|
44
|
+
|
|
19
45
|
* Implemented parallel processing for pyramid stacking algorithm
|
|
20
46
|
* optimized pyramid algorithm: selects automatically the best within the given memory budget to avoid memory issues in case many pictures are selected. Explicit configuration is also possible for specific needs.
|
|
21
47
|
* Implemented automatic subsample option for alignment, balancing and vignetting, now default
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shinestacker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: ShineStacker
|
|
5
5
|
Author-email: Luca Lista <luka.lista@gmail.com>
|
|
6
6
|
License-Expression: LGPL-3.0
|
|
@@ -20,6 +20,7 @@ Requires-Dist: numpy
|
|
|
20
20
|
Requires-Dist: opencv_python
|
|
21
21
|
Requires-Dist: pillow
|
|
22
22
|
Requires-Dist: psdtags
|
|
23
|
+
Requires-Dist: psutil
|
|
23
24
|
Requires-Dist: PySide6
|
|
24
25
|
Requires-Dist: scipy
|
|
25
26
|
Requires-Dist: tifffile
|
|
@@ -69,6 +70,10 @@ The GUI has two main working areas:
|
|
|
69
70
|
|
|
70
71
|
<img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/gui-retouch.png' width="600" referrerpolicy="no-referrer">
|
|
71
72
|
|
|
73
|
+
# Resources
|
|
74
|
+
|
|
75
|
+
🌍 [Website on WordPress](https://shinestacker.wordpress.com) • 📖 [Main documentation](https://shinestacker.readthedocs.io) • 📝 [Changelog](https://github.com/lucalista/shinestacker/blob/main/CHANGELOG.md)
|
|
76
|
+
|
|
72
77
|
# Note for macOS users
|
|
73
78
|
|
|
74
79
|
**The following note is only relevant if you download the application as compressed archive from the [release page](https://github.com/lucalista/shinestacker/releases).**
|
|
@@ -88,11 +93,6 @@ xattr -cr ~/Downloads/shinestacker/shinestacker.app
|
|
|
88
93
|
|
|
89
94
|
macOS adds a quarantine flag to all files downloaded from the internet. The above command removes that flag while preserving all other application functionality.
|
|
90
95
|
|
|
91
|
-
# Resources
|
|
92
|
-
|
|
93
|
-
🌍 [Website on WordPress](https://shinestacker.wordpress.com) • 📖 [Main documentation](https://shinestacker.readthedocs.io) • 📝 [Changelog](https://github.com/lucalista/shinestacker/blob/main/CHANGELOG.md)
|
|
94
|
-
|
|
95
|
-
|
|
96
96
|
# Credits
|
|
97
97
|
|
|
98
98
|
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.
|
|
@@ -38,6 +38,10 @@ The GUI has two main working areas:
|
|
|
38
38
|
|
|
39
39
|
<img src='https://raw.githubusercontent.com/lucalista/shinestacker/main/img/gui-retouch.png' width="600" referrerpolicy="no-referrer">
|
|
40
40
|
|
|
41
|
+
# Resources
|
|
42
|
+
|
|
43
|
+
🌍 [Website on WordPress](https://shinestacker.wordpress.com) • 📖 [Main documentation](https://shinestacker.readthedocs.io) • 📝 [Changelog](https://github.com/lucalista/shinestacker/blob/main/CHANGELOG.md)
|
|
44
|
+
|
|
41
45
|
# Note for macOS users
|
|
42
46
|
|
|
43
47
|
**The following note is only relevant if you download the application as compressed archive from the [release page](https://github.com/lucalista/shinestacker/releases).**
|
|
@@ -57,11 +61,6 @@ xattr -cr ~/Downloads/shinestacker/shinestacker.app
|
|
|
57
61
|
|
|
58
62
|
macOS adds a quarantine flag to all files downloaded from the internet. The above command removes that flag while preserving all other application functionality.
|
|
59
63
|
|
|
60
|
-
# Resources
|
|
61
|
-
|
|
62
|
-
🌍 [Website on WordPress](https://shinestacker.wordpress.com) • 📖 [Main documentation](https://shinestacker.readthedocs.io) • 📝 [Changelog](https://github.com/lucalista/shinestacker/blob/main/CHANGELOG.md)
|
|
63
|
-
|
|
64
|
-
|
|
65
64
|
# Credits
|
|
66
65
|
|
|
67
66
|
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.
|
|
@@ -93,6 +93,14 @@ alignment_config = {
|
|
|
93
93
|
* ```plot_matches``` (optional, default: ```False```): if ```True```, for each image matches with reference frame are drawn. May be useful for inspection and debugging.
|
|
94
94
|
* ```enabled``` (optional, default: ```True```): allows to switch on and off this module.
|
|
95
95
|
|
|
96
|
+
## Parallel processing
|
|
97
|
+
|
|
98
|
+
A class ```AlignFramesParallel``` implements alignment using parallel processing.
|
|
99
|
+
This class has extra parameters, in addition to the above ones:
|
|
100
|
+
|
|
101
|
+
* ```max_threads``` (optional, default: ```2```): number of parallel processes allowed. The number of actual threads will not be greater than the number of available CPU cores.
|
|
102
|
+
* ```chunk_submit``` (optional, default: ```True```): submit at most ```max_threads``` parallel processes. If ```chunk_submit``` is greater than ```max_threads``` a moderate performance gain is achieved at the cost of a possibly large memory occupancy.
|
|
103
|
+
* ```bw_matching``` (optional, default: ```False```): perform matches on black and white version of the images in order to save memory. Preliminary tests indicate that the gain with this option is marginal, and this option may be dropped in the future.
|
|
96
104
|
|
|
97
105
|
## Allowed configurations
|
|
98
106
|
|
|
@@ -48,6 +48,7 @@ Arguments for the constructor are, in addition to the ones for ```PyramidStack``
|
|
|
48
48
|
* ```tile_size``` (optional, default: 512): size of a time
|
|
49
49
|
* ```n_tiled_layers``` (optional, default: 2): number of layers that are tiled. Usually the last one or two are the ones that take more memory.
|
|
50
50
|
* ```max_threads``` (optional, default: number of cores, up to a maximum of 8): maximum number of thread used for parallel processing. The actual number of threads does not exceed the number of available cores.
|
|
51
|
+
* ```chunk_submit``` (optional, default: ```True```): submit at most ```max_threads``` parallel processes. If ```chunk_submit``` is greater than ```max_threads``` a moderate performance gain is achieved at the cost of a possibly large memory occupancy.
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
```PyramidAutoStack```, pyramid algorithn with capability to automatically switch from all-in-memory to I/O buffered tiled.
|
|
@@ -44,3 +44,5 @@ Arguments for the constructor of ```CombinedActions``` are for the :
|
|
|
44
44
|
* ```reference_index``` (optional, default: 0): the index of the image used as reference. Images are numbered starting from one to N. -1 is interpreted as the last image, 0 as the median index.
|
|
45
45
|
* ```step_process``` (optional): if equal to ```True``` (default), each image is processed with respect to the previous or next image, depending if its file is placed in alphabetic order after or befor the reference image.
|
|
46
46
|
* ```enabled``` (optional, default: ```True```): allows to switch on and off this module.
|
|
47
|
+
* ```max_threads``` (optional, default: ```2```): number of parallel processes allowed. The number of actual threads will not be greater than the number of available CPU cores.
|
|
48
|
+
* ```chunk_submit``` (optional, default: ```True```): submit at most ```max_threads``` parallel processes. If ```chunk_submit``` is greater than ```max_threads``` a moderate performance gain is achieved at the cost of a possibly large memory occupancy.
|
|
@@ -87,7 +87,7 @@ job.run()
|
|
|
87
87
|
|
|
88
88
|
### Core processing
|
|
89
89
|
```bash
|
|
90
|
-
pip install imagecodecs matplotlib numpy opencv-python pillow psdtags scipy setuptools-scm tifffile tqdm
|
|
90
|
+
pip install imagecodecs matplotlib numpy opencv-python pillow psdtags psutil scipy setuptools-scm tifffile tqdm
|
|
91
91
|
```
|
|
92
92
|
## GUI support
|
|
93
93
|
```bash
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '1.3.0'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, E1101, R0914, R0913, R0917, R0912, R0915, R0902, E1121, W0102
|
|
2
|
-
import
|
|
2
|
+
import os
|
|
3
3
|
import math
|
|
4
|
+
import logging
|
|
4
5
|
import numpy as np
|
|
5
6
|
import matplotlib.pyplot as plt
|
|
6
7
|
import cv2
|
|
@@ -20,7 +21,7 @@ _DEFAULT_MATCHING_CONFIG = {
|
|
|
20
21
|
'flann_idx_kdtree': constants.DEFAULT_FLANN_IDX_KDTREE,
|
|
21
22
|
'flann_trees': constants.DEFAULT_FLANN_TREES,
|
|
22
23
|
'flann_checks': constants.DEFAULT_FLANN_CHECKS,
|
|
23
|
-
'threshold': constants.DEFAULT_ALIGN_THRESHOLD
|
|
24
|
+
'threshold': constants.DEFAULT_ALIGN_THRESHOLD,
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
_DEFAULT_ALIGNMENT_CONFIG = {
|
|
@@ -130,6 +131,17 @@ def check_homography_distortion(m, img_shape, homography_thresholds=_HOMOGRAPHY_
|
|
|
130
131
|
return True, "Transformation within acceptable limits"
|
|
131
132
|
|
|
132
133
|
|
|
134
|
+
def check_transform(m, img_0, transform_type,
|
|
135
|
+
affine_thresholds, homography_thresholds):
|
|
136
|
+
if transform_type == constants.ALIGN_RIGID:
|
|
137
|
+
return check_affine_matrix(
|
|
138
|
+
m, img_0.shape, affine_thresholds)
|
|
139
|
+
if transform_type == constants.ALIGN_HOMOGRAPHY:
|
|
140
|
+
return check_homography_distortion(
|
|
141
|
+
m, img_0.shape, homography_thresholds)
|
|
142
|
+
return False, f'invalid transfrom option {transform_type}'
|
|
143
|
+
|
|
144
|
+
|
|
133
145
|
def get_good_matches(des_0, des_ref, matching_config=None):
|
|
134
146
|
matching_config = {**_DEFAULT_MATCHING_CONFIG, **(matching_config or {})}
|
|
135
147
|
match_method = matching_config['match_method']
|
|
@@ -172,7 +184,23 @@ def validate_align_config(detector, descriptor, match_method):
|
|
|
172
184
|
" require matching method Hamming distance")
|
|
173
185
|
|
|
174
186
|
|
|
175
|
-
|
|
187
|
+
detector_map = {
|
|
188
|
+
constants.DETECTOR_SIFT: cv2.SIFT_create,
|
|
189
|
+
constants.DETECTOR_ORB: cv2.ORB_create,
|
|
190
|
+
constants.DETECTOR_SURF: cv2.FastFeatureDetector_create,
|
|
191
|
+
constants.DETECTOR_AKAZE: cv2.AKAZE_create,
|
|
192
|
+
constants.DETECTOR_BRISK: cv2.BRISK_create
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
descriptor_map = {
|
|
196
|
+
constants.DESCRIPTOR_SIFT: cv2.SIFT_create,
|
|
197
|
+
constants.DESCRIPTOR_ORB: cv2.ORB_create,
|
|
198
|
+
constants.DESCRIPTOR_AKAZE: cv2.AKAZE_create,
|
|
199
|
+
constants.DETECTOR_BRISK: cv2.BRISK_create
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def detect_and_compute_matches(img_ref, img_0, feature_config=None, matching_config=None):
|
|
176
204
|
feature_config = {**_DEFAULT_FEATURE_CONFIG, **(feature_config or {})}
|
|
177
205
|
matching_config = {**_DEFAULT_MATCHING_CONFIG, **(matching_config or {})}
|
|
178
206
|
feature_config_detector = feature_config['detector']
|
|
@@ -180,19 +208,6 @@ def detect_and_compute(img_0, img_ref, feature_config=None, matching_config=None
|
|
|
180
208
|
match_method = matching_config['match_method']
|
|
181
209
|
validate_align_config(feature_config_detector, feature_config_descriptor, match_method)
|
|
182
210
|
img_bw_0, img_bw_ref = img_bw_8bit(img_0), img_bw_8bit(img_ref)
|
|
183
|
-
detector_map = {
|
|
184
|
-
constants.DETECTOR_SIFT: cv2.SIFT_create,
|
|
185
|
-
constants.DETECTOR_ORB: cv2.ORB_create,
|
|
186
|
-
constants.DETECTOR_SURF: cv2.FastFeatureDetector_create,
|
|
187
|
-
constants.DETECTOR_AKAZE: cv2.AKAZE_create,
|
|
188
|
-
constants.DETECTOR_BRISK: cv2.BRISK_create
|
|
189
|
-
}
|
|
190
|
-
descriptor_map = {
|
|
191
|
-
constants.DESCRIPTOR_SIFT: cv2.SIFT_create,
|
|
192
|
-
constants.DESCRIPTOR_ORB: cv2.ORB_create,
|
|
193
|
-
constants.DESCRIPTOR_AKAZE: cv2.AKAZE_create,
|
|
194
|
-
constants.DETECTOR_BRISK: cv2.BRISK_create
|
|
195
|
-
}
|
|
196
211
|
detector = detector_map[feature_config_detector]()
|
|
197
212
|
if feature_config_detector == feature_config_descriptor and \
|
|
198
213
|
feature_config_detector in (constants.DETECTOR_SIFT,
|
|
@@ -236,6 +251,25 @@ def find_transform(src_pts, dst_pts, transform=constants.DEFAULT_TRANSFORM,
|
|
|
236
251
|
return result
|
|
237
252
|
|
|
238
253
|
|
|
254
|
+
def rescale_trasnsform(m, w0, h0, w_sub, h_sub, subsample, transform):
|
|
255
|
+
if transform == constants.ALIGN_HOMOGRAPHY:
|
|
256
|
+
low_size = np.float32([[0, 0], [0, h_sub], [w_sub, h_sub], [w_sub, 0]])
|
|
257
|
+
high_size = np.float32([[0, 0], [0, h0], [w0, h0], [w0, 0]])
|
|
258
|
+
scale_up = cv2.getPerspectiveTransform(low_size, high_size)
|
|
259
|
+
scale_down = cv2.getPerspectiveTransform(high_size, low_size)
|
|
260
|
+
m = scale_up @ m @ scale_down
|
|
261
|
+
elif transform == constants.ALIGN_RIGID:
|
|
262
|
+
rotation = m[:2, :2]
|
|
263
|
+
translation = m[:, 2]
|
|
264
|
+
translation_fullres = translation * subsample
|
|
265
|
+
m = np.empty((2, 3), dtype=np.float32)
|
|
266
|
+
m[:2, :2] = rotation
|
|
267
|
+
m[:, 2] = translation_fullres
|
|
268
|
+
else:
|
|
269
|
+
return 0
|
|
270
|
+
return m
|
|
271
|
+
|
|
272
|
+
|
|
239
273
|
def align_images(img_ref, img_0, feature_config=None, matching_config=None, alignment_config=None,
|
|
240
274
|
plot_path=None, callbacks=None,
|
|
241
275
|
affine_thresholds=_AFFINE_THRESHOLDS,
|
|
@@ -265,8 +299,8 @@ def align_images(img_ref, img_0, feature_config=None, matching_config=None, alig
|
|
|
265
299
|
img_ref_sub = img_subsample(img_ref, subsample, fast_subsampling)
|
|
266
300
|
else:
|
|
267
301
|
img_0_sub, img_ref_sub = img_0, img_ref
|
|
268
|
-
kp_0, kp_ref, good_matches =
|
|
269
|
-
|
|
302
|
+
kp_0, kp_ref, good_matches = detect_and_compute_matches(
|
|
303
|
+
img_ref_sub, img_0_sub, feature_config, matching_config)
|
|
270
304
|
n_good_matches = len(good_matches)
|
|
271
305
|
if n_good_matches > min_good_matches or subsample == 1:
|
|
272
306
|
break
|
|
@@ -301,34 +335,18 @@ def align_images(img_ref, img_0, feature_config=None, matching_config=None, alig
|
|
|
301
335
|
callbacks['save_plot'](plot_path)
|
|
302
336
|
h_sub, w_sub = img_0_sub.shape[:2]
|
|
303
337
|
if subsample > 1:
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
high_size = np.float32([[0, 0], [0, h0], [w0, h0], [w0, 0]])
|
|
307
|
-
scale_up = cv2.getPerspectiveTransform(low_size, high_size)
|
|
308
|
-
scale_down = cv2.getPerspectiveTransform(high_size, low_size)
|
|
309
|
-
m = scale_up @ m @ scale_down
|
|
310
|
-
elif transform == constants.ALIGN_RIGID:
|
|
311
|
-
rotation = m[:2, :2]
|
|
312
|
-
translation = m[:, 2]
|
|
313
|
-
translation_fullres = translation * subsample
|
|
314
|
-
m = np.empty((2, 3), dtype=np.float32)
|
|
315
|
-
m[:2, :2] = rotation
|
|
316
|
-
m[:, 2] = translation_fullres
|
|
317
|
-
else:
|
|
338
|
+
m = rescale_trasnsform(m, w0, h0, w_sub, h_sub, subsample, transform)
|
|
339
|
+
if m is None:
|
|
318
340
|
raise InvalidOptionError("transform", transform)
|
|
319
|
-
|
|
320
341
|
transform_type = alignment_config['transform']
|
|
321
|
-
is_valid =
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
is_valid, reason = check_affine_matrix(
|
|
325
|
-
m, img_0.shape, affine_thresholds)
|
|
326
|
-
elif transform_type == constants.ALIGN_HOMOGRAPHY:
|
|
327
|
-
is_valid, reason = check_homography_distortion(
|
|
328
|
-
m, img_0.shape, homography_thresholds)
|
|
342
|
+
is_valid, reason = check_transform(
|
|
343
|
+
m, img_0, transform_type,
|
|
344
|
+
affine_thresholds, homography_thresholds)
|
|
329
345
|
if not is_valid:
|
|
330
346
|
if callbacks and 'warning' in callbacks:
|
|
331
347
|
callbacks['warning'](f"invalid transformation: {reason}")
|
|
348
|
+
if alignment_config['abort_abnormal']:
|
|
349
|
+
raise RuntimeError("invalid transformation: {reason}")
|
|
332
350
|
return n_good_matches, None, None
|
|
333
351
|
if callbacks and 'align_message' in callbacks:
|
|
334
352
|
callbacks['align_message']()
|
|
@@ -357,12 +375,12 @@ def align_images(img_ref, img_0, feature_config=None, matching_config=None, alig
|
|
|
357
375
|
return n_good_matches, m, img_warp
|
|
358
376
|
|
|
359
377
|
|
|
360
|
-
class
|
|
378
|
+
class AlignFramesBase(SubAction):
|
|
361
379
|
def __init__(self, enabled=True, feature_config=None, matching_config=None,
|
|
362
380
|
alignment_config=None, **kwargs):
|
|
363
381
|
super().__init__(enabled)
|
|
364
382
|
self.process = None
|
|
365
|
-
self.
|
|
383
|
+
self._n_good_matches = None
|
|
366
384
|
self.feature_config = {**_DEFAULT_FEATURE_CONFIG, **(feature_config or {})}
|
|
367
385
|
self.matching_config = {**_DEFAULT_MATCHING_CONFIG, **(matching_config or {})}
|
|
368
386
|
self.alignment_config = {**_DEFAULT_ALIGNMENT_CONFIG, **(alignment_config or {})}
|
|
@@ -380,69 +398,36 @@ class AlignFrames(SubAction):
|
|
|
380
398
|
if k in kwargs:
|
|
381
399
|
self.alignment_config[k] = kwargs[k]
|
|
382
400
|
|
|
401
|
+
def align_images(self, idx, img_ref, img_0):
|
|
402
|
+
pass
|
|
403
|
+
|
|
404
|
+
def print_message(self, msg, color=constants.LOG_COLOR_LEVEL_3, level=logging.INFO):
|
|
405
|
+
self.process.print_message(color_str(msg, color), level=level)
|
|
406
|
+
|
|
407
|
+
def begin(self, process):
|
|
408
|
+
self.process = process
|
|
409
|
+
self._n_good_matches = np.zeros(process.total_action_counts)
|
|
410
|
+
|
|
383
411
|
def run_frame(self, idx, ref_idx, img_0):
|
|
384
412
|
if idx == self.process.ref_idx:
|
|
385
413
|
return img_0
|
|
386
414
|
img_ref = self.process.img_ref(ref_idx)
|
|
387
415
|
return self.align_images(idx, img_ref, img_0)
|
|
388
416
|
|
|
389
|
-
def
|
|
390
|
-
|
|
417
|
+
def get_transform_thresholds(self):
|
|
418
|
+
return _AFFINE_THRESHOLDS, _HOMOGRAPHY_THRESHOLDS
|
|
391
419
|
|
|
392
|
-
def
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
'message': lambda: self.sub_msg(': find matches'),
|
|
396
|
-
'matches_message': lambda n: self.sub_msg(f": good matches: {n}"),
|
|
397
|
-
'align_message': lambda: self.sub_msg(': align images'),
|
|
398
|
-
'ecc_message': lambda: self.sub_msg(": ecc refinement"),
|
|
399
|
-
'blur_message': lambda: self.sub_msg(': blur borders'),
|
|
400
|
-
'warning': lambda msg: self.sub_msg(
|
|
401
|
-
f': {msg}', constants.LOG_COLOR_WARNING),
|
|
402
|
-
'save_plot': lambda plot_path: self.process.callback(
|
|
403
|
-
'save_plot', self.process.id,
|
|
404
|
-
f"{self.process.name}: matches\nframe {idx_str}", plot_path)
|
|
405
|
-
}
|
|
406
|
-
if self.plot_matches:
|
|
407
|
-
plot_path = f"{self.process.working_path}/{self.process.plot_path}/" \
|
|
408
|
-
f"{self.process.name}-matches-{idx_str}.pdf"
|
|
409
|
-
else:
|
|
410
|
-
plot_path = None
|
|
411
|
-
if self.alignment_config['abort_abnormal']:
|
|
412
|
-
affine_thresholds = _AFFINE_THRESHOLDS
|
|
413
|
-
homography_thresholds = _HOMOGRAPHY_THRESHOLDS
|
|
414
|
-
else:
|
|
415
|
-
affine_thresholds = None
|
|
416
|
-
homography_thresholds = None
|
|
417
|
-
n_good_matches, _m, img = align_images(
|
|
418
|
-
img_ref, img_0,
|
|
419
|
-
feature_config=self.feature_config,
|
|
420
|
-
matching_config=self.matching_config,
|
|
421
|
-
alignment_config=self.alignment_config,
|
|
422
|
-
plot_path=plot_path,
|
|
423
|
-
callbacks=callbacks,
|
|
424
|
-
affine_thresholds=affine_thresholds,
|
|
425
|
-
homography_thresholds=homography_thresholds
|
|
426
|
-
)
|
|
427
|
-
self.n_matches[idx] = n_good_matches
|
|
428
|
-
if n_good_matches < self.min_matches:
|
|
429
|
-
self.process.sub_message(color_str(f": image not aligned, too few matches found: "
|
|
430
|
-
f"{n_good_matches}", constants.LOG_COLOR_WARNING),
|
|
431
|
-
level=logging.WARNING)
|
|
432
|
-
return None
|
|
433
|
-
return img
|
|
434
|
-
|
|
435
|
-
def begin(self, process):
|
|
436
|
-
self.process = process
|
|
437
|
-
self.n_matches = np.zeros(process.total_action_counts)
|
|
420
|
+
def image_str(self, idx):
|
|
421
|
+
return f"image: {self.process.idx_tot_str(idx)}, " \
|
|
422
|
+
f"{os.path.basename(self.process.input_filepath(idx))}"
|
|
438
423
|
|
|
439
424
|
def end(self):
|
|
440
425
|
if self.plot_summary:
|
|
441
426
|
plt.figure(figsize=constants.PLT_FIG_SIZE)
|
|
442
|
-
x = np.arange(1, len(self.
|
|
427
|
+
x = np.arange(1, len(self._n_good_matches) + 1, dtype=int)
|
|
443
428
|
no_ref = x != self.process.ref_idx + 1
|
|
444
429
|
x = x[no_ref]
|
|
445
|
-
y = self.
|
|
430
|
+
y = self._n_good_matches[no_ref]
|
|
446
431
|
if self.process.ref_idx == 0:
|
|
447
432
|
y_max = y[1]
|
|
448
433
|
elif self.process.ref_idx >= len(y):
|
|
@@ -464,5 +449,52 @@ class AlignFrames(SubAction):
|
|
|
464
449
|
f"{self.process.name}-matches.pdf"
|
|
465
450
|
save_plot(plot_path)
|
|
466
451
|
plt.close('all')
|
|
467
|
-
self.process.callback(
|
|
452
|
+
self.process.callback(constants.CALLBACK_SAVE_PLOT, self.process.id,
|
|
468
453
|
f"{self.process.name}: matches", plot_path)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
class AlignFrames(AlignFramesBase):
|
|
457
|
+
def __init__(self, enabled=True, feature_config=None, matching_config=None,
|
|
458
|
+
alignment_config=None, **kwargs):
|
|
459
|
+
super().__init__(enabled)
|
|
460
|
+
|
|
461
|
+
def align_images(self, idx, img_ref, img_0):
|
|
462
|
+
idx_str = f"{idx:04d}"
|
|
463
|
+
idx_tot_str = self.process.idx_tot_str(idx)
|
|
464
|
+
callbacks = {
|
|
465
|
+
'message': lambda: self.print_message(f'{idx_tot_str}: find matches'),
|
|
466
|
+
'matches_message': lambda n: self.print_message(f'{idx_tot_str}: good matches: {n}'),
|
|
467
|
+
'align_message': lambda: self.print_message(f'{idx_tot_str}: align images'),
|
|
468
|
+
'blur_message': lambda: self.print_message(f'{idx_tot_str}: blur borders'),
|
|
469
|
+
'warning': lambda msg: self.print_message(
|
|
470
|
+
f': {msg}', constants.LOG_COLOR_WARNING),
|
|
471
|
+
'save_plot': lambda plot_path: self.process.callback(
|
|
472
|
+
constants.CALLBACK_SAVE_PLOT, self.process.id,
|
|
473
|
+
f"{self.process.name}: matches\nframe {idx_str}", plot_path)
|
|
474
|
+
}
|
|
475
|
+
if self.plot_matches:
|
|
476
|
+
plot_path = f"{self.process.working_path}/{self.process.plot_path}/" \
|
|
477
|
+
f"{self.process.name}-matches-{idx_str}.pdf"
|
|
478
|
+
else:
|
|
479
|
+
plot_path = None
|
|
480
|
+
affine_thresholds, homography_thresholds = self.get_transform_thresholds()
|
|
481
|
+
n_good_matches, _m, img = align_images(
|
|
482
|
+
img_ref, img_0,
|
|
483
|
+
feature_config=self.feature_config,
|
|
484
|
+
matching_config=self.matching_config,
|
|
485
|
+
alignment_config=self.alignment_config,
|
|
486
|
+
plot_path=plot_path,
|
|
487
|
+
callbacks=callbacks,
|
|
488
|
+
affine_thresholds=affine_thresholds,
|
|
489
|
+
homography_thresholds=homography_thresholds
|
|
490
|
+
)
|
|
491
|
+
self._n_good_matches[idx] = n_good_matches
|
|
492
|
+
if n_good_matches < self.min_matches:
|
|
493
|
+
self.process.print_message(
|
|
494
|
+
f"{self.image_str(idx)} not aligned, too few matches found: "
|
|
495
|
+
f"{n_good_matches}")
|
|
496
|
+
return None
|
|
497
|
+
return img
|
|
498
|
+
|
|
499
|
+
def sequential_processing(self):
|
|
500
|
+
return True
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# pylint: disable=C0114, C0115, C0116, W0718, R0912, R0915, E1101, R0914, R0911, E0606, R0801, R0902
|
|
2
|
+
import os
|
|
3
|
+
from ..config.constants import constants
|
|
4
|
+
from .align import AlignFramesBase, AlignFrames
|
|
5
|
+
from .align_parallel import AlignFramesParallel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AlignFramesAuto(AlignFramesBase):
|
|
9
|
+
def __init__(self, enabled=True, feature_config=None, matching_config=None,
|
|
10
|
+
alignment_config=None, **kwargs):
|
|
11
|
+
super().__init__(enabled=True, feature_config=None, matching_config=None,
|
|
12
|
+
alignment_config=None, **kwargs)
|
|
13
|
+
self.mode = kwargs.pop('mode', constants.DEFAULT_ALIGN_MODE)
|
|
14
|
+
self.max_threads = kwargs.pop('max_threads', constants.DEFAULT_ALIGN_MAX_THREADS)
|
|
15
|
+
self.chunk_submit = kwargs.pop('chunk_submit', constants.DEFAULT_ALIGN_CHUNK_SUBMIT)
|
|
16
|
+
self.bw_matching = kwargs.pop('bw_matching', constants.DEFAULT_ALIGN_BW_MATCHING)
|
|
17
|
+
self.kwargs = kwargs
|
|
18
|
+
available_cores = os.cpu_count() or 1
|
|
19
|
+
self.num_threads = min(self.max_threads, available_cores)
|
|
20
|
+
self._implementation = None
|
|
21
|
+
|
|
22
|
+
def begin(self, process):
|
|
23
|
+
if self.mode == 'sequential' or self.num_threads == 1:
|
|
24
|
+
self._implementation = AlignFrames(
|
|
25
|
+
self.enabled, self.feature_config, self.matching_config, self.alignment_config,
|
|
26
|
+
**self.kwargs)
|
|
27
|
+
else:
|
|
28
|
+
if self.mode == 'parallel':
|
|
29
|
+
num_threads = self.num_threads
|
|
30
|
+
chunk_submit = self.chunk_submit
|
|
31
|
+
else:
|
|
32
|
+
if self.feature_config is not None:
|
|
33
|
+
detector = self.feature_config.get(
|
|
34
|
+
'detector', constants.DEFAULT_DETECTOR)
|
|
35
|
+
descriptor = self.feature_config.get(
|
|
36
|
+
'descriptor', constants.DEFAULT_DESCRIPTOR)
|
|
37
|
+
else:
|
|
38
|
+
detector = constants.DEFAULT_DETECTOR
|
|
39
|
+
descriptor = constants.DEFAULT_DESCRIPTOR
|
|
40
|
+
if detector in (constants.DETECTOR_SIFT, constants.DETECTOR_AKAZE) or \
|
|
41
|
+
descriptor in (constants.DESCRIPTOR_SIFT, constants.DESCRIPTOR_AKAZE):
|
|
42
|
+
num_threads = min(3, self.num_threads)
|
|
43
|
+
chunk_submit = True
|
|
44
|
+
else:
|
|
45
|
+
num_threads = self.num_threads
|
|
46
|
+
chunk_submit = self.chunk_submit
|
|
47
|
+
self._implementation = AlignFramesParallel(
|
|
48
|
+
self.enabled, self.feature_config, self.matching_config, self.alignment_config,
|
|
49
|
+
max_threads=num_threads, chunk_submit=chunk_submit,
|
|
50
|
+
bw_matching=self.bw_matching,
|
|
51
|
+
**self.kwargs)
|
|
52
|
+
self._implementation.begin(process)
|
|
53
|
+
|
|
54
|
+
def align_images(self, idx, img_ref, img_0):
|
|
55
|
+
return self._implementation.align_images(idx, img_ref, img_0)
|
|
56
|
+
|
|
57
|
+
def run_frame(self, idx, ref_idx, img_0):
|
|
58
|
+
return self._implementation.run_frame(idx, ref_idx, img_0)
|
|
59
|
+
|
|
60
|
+
def sequential_processing(self):
|
|
61
|
+
return self._implementation.sequential_processing()
|
|
62
|
+
|
|
63
|
+
def end(self):
|
|
64
|
+
self._implementation.end()
|