shinestacker 1.0.3__tar.gz → 1.0.4.post2__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.0.3 → shinestacker-1.0.4.post2}/.github/workflows/release.yml +6 -6
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/CHANGELOG.md +18 -0
- {shinestacker-1.0.3/src/shinestacker.egg-info → shinestacker-1.0.4.post2}/PKG-INFO +1 -1
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/scripts/build_release.py +17 -6
- shinestacker-1.0.4.post2/src/shinestacker/_version.py +1 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/exif.py +10 -13
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/multilayer.py +5 -4
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/utils.py +47 -7
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/app/main.py +99 -2
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/gui_run.py +5 -4
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/new_project.py +6 -11
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/file_loader.py +3 -4
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/io_gui_handler.py +3 -7
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2/src/shinestacker.egg-info}/PKG-INFO +1 -1
- shinestacker-1.0.3/src/shinestacker/_version.py +0 -1
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/.coveragerc +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/.flake8 +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/.github/workflows/ci-multiplatform.yml +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/.github/workflows/pylint.yml +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/.github/workflows/pypi-publish.yml +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/.gitignore +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/.pylintrc +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/.readthedocs.yaml +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/LICENSE +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/MANIFEST.in +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/README.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/THIRD_PARTY_LICENSES.txt +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/alignment.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/api.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/balancing.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/conf.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/focus_stacking.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/gui.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/index.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/job.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/main.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/multilayer.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/noise.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/requirements.txt +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/docs/vignetting.md +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/img/coffee.gif +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/img/coffee_stack.jpg +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/img/extreme-vignetting.jpg +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/img/flies.gif +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/img/flies_stack.jpg +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/img/flow-diagram.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/img/gui-finder.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/img/gui-project-new.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/img/gui-project-run.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/img/gui-retouch.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/pyproject.toml +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/requirements.txt +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/scripts/git-rev-list.sh +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/scripts/validate-tomli.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/setup.cfg +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/__init__.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/__init__.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/align.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/balance.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/base_stack_algo.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/denoise.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/depth_map.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/noise_detection.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/pyramid.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/sharpen.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/stack.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/stack_framework.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/vignetting.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/white_balance.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/app/__init__.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/app/about_dialog.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/app/gui_utils.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/app/help_menu.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/app/open_frames.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/app/project.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/app/retouch.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/config/__init__.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/config/config.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/config/constants.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/config/gui_constants.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/core/__init__.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/core/colors.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/core/core_utils.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/core/exceptions.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/core/framework.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/core/logging.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/__init__.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/action_config.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/action_config_dialog.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/base_form_dialog.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/colors.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/gui_images.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/gui_logging.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/ico/focus_stack_bkg.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/ico/shinestacker.icns +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/ico/shinestacker.ico +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/ico/shinestacker.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/ico/shinestacker.svg +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/img/close-round-line-icon.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/img/forward-button-icon.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/img/play-button-round-icon.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/img/plus-round-line-icon.png +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/main_window.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/menu_manager.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/project_controller.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/project_converter.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/project_editor.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/project_model.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/select_path_widget.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/tab_widget.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/time_progress_bar.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/__init__.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/base_filter.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/brush.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/brush_gradient.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/brush_preview.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/brush_tool.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/denoise_filter.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/display_manager.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/exif_data.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/filter_manager.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/icon_container.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/image_editor_ui.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/image_viewer.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/io_manager.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/layer_collection.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/shortcuts_help.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/undo_manager.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/unsharp_mask_filter.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/vignetting_filter.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/white_balance_filter.py +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker.egg-info/SOURCES.txt +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker.egg-info/dependency_links.txt +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker.egg-info/entry_points.txt +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker.egg-info/requires.txt +0 -0
- {shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker.egg-info/top_level.txt +0 -0
|
@@ -44,8 +44,8 @@ jobs:
|
|
|
44
44
|
uses: actions/upload-artifact@v4
|
|
45
45
|
with:
|
|
46
46
|
name: shinestacker-${{ matrix.os }}
|
|
47
|
-
path:
|
|
48
|
-
|
|
47
|
+
path: |
|
|
48
|
+
${{ matrix.os == 'windows-latest' && 'dist/shinestacker-release.zip' || 'dist/shinestacker-release.tar.gz' }}
|
|
49
49
|
create-release:
|
|
50
50
|
needs: publish-release
|
|
51
51
|
runs-on: ubuntu-latest
|
|
@@ -58,9 +58,9 @@ jobs:
|
|
|
58
58
|
- name: Prepare release assets
|
|
59
59
|
run: |
|
|
60
60
|
mkdir -p release_assets
|
|
61
|
-
cp artifacts/shinestacker-ubuntu-latest/shinestacker-release.
|
|
61
|
+
cp artifacts/shinestacker-ubuntu-latest/shinestacker-release.tar.gz release_assets/shinestacker-ubuntu.tar.gz
|
|
62
|
+
cp artifacts/shinestacker-macos-latest/shinestacker-release.tar.gz release_assets/shinestacker-macos.tar.gz
|
|
62
63
|
cp artifacts/shinestacker-windows-latest/shinestacker-release.zip release_assets/shinestacker-windows.zip
|
|
63
|
-
cp artifacts/shinestacker-macos-latest/shinestacker-release.zip release_assets/shinestacker-macos.zip
|
|
64
64
|
ls -la release_assets/
|
|
65
65
|
|
|
66
66
|
- name: Create release
|
|
@@ -68,6 +68,6 @@ jobs:
|
|
|
68
68
|
with:
|
|
69
69
|
draft: true
|
|
70
70
|
files: |
|
|
71
|
-
release_assets/shinestacker-ubuntu.
|
|
71
|
+
release_assets/shinestacker-ubuntu.tar.gz
|
|
72
|
+
release_assets/shinestacker-macos.tar.gz
|
|
72
73
|
release_assets/shinestacker-windows.zip
|
|
73
|
-
release_assets/shinestacker-macos.zip
|
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
This page reports the main releases only and the main changes therein.
|
|
4
4
|
|
|
5
|
+
## [v-.-.-] - 2025-08-26
|
|
6
|
+
**Bug fixes**
|
|
7
|
+
|
|
8
|
+
### Changes
|
|
9
|
+
|
|
10
|
+
* fixed release build script changing format from zip to tar.gz for macOs and Linux
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## [v1.0.4] - 2025-08-26
|
|
15
|
+
**Bug fixes**
|
|
16
|
+
|
|
17
|
+
### Changes
|
|
18
|
+
|
|
19
|
+
* extensions are treated in lower case (e.g.: both jpg and JPG)
|
|
20
|
+
* added missing retouch menu action: import frames from current project
|
|
21
|
+
|
|
22
|
+
---
|
|
5
23
|
## [v1.0.3] - 2025-08-26
|
|
6
24
|
**Bug fixes**
|
|
7
25
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import shutil
|
|
3
|
+
import tarfile
|
|
3
4
|
import subprocess
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
import platform
|
|
@@ -35,9 +36,19 @@ for project_file in ["complete-project.fsp", "stack-from-frames.fsp"]:
|
|
|
35
36
|
shutil.copy(examples_dir / project_file, target_examples)
|
|
36
37
|
shutil.copytree(examples_dir / 'input', target_examples / 'input', dirs_exist_ok=True)
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
if sys_name == 'windows':
|
|
40
|
+
shutil.make_archive(
|
|
41
|
+
base_name=str(dist_dir / "shinestacker-release"),
|
|
42
|
+
format="zip",
|
|
43
|
+
root_dir=dist_dir,
|
|
44
|
+
base_dir=package_dir
|
|
45
|
+
)
|
|
46
|
+
else:
|
|
47
|
+
archive_path = dist_dir / "shinestacker-release.tar.gz"
|
|
48
|
+
with tarfile.open(archive_path, "w:gz") as tar:
|
|
49
|
+
tar.add(
|
|
50
|
+
dist_dir / package_dir,
|
|
51
|
+
arcname=package_dir,
|
|
52
|
+
recursive=True,
|
|
53
|
+
filter=lambda info: info
|
|
54
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '1.0.4.post2'
|
|
@@ -10,7 +10,7 @@ from PIL.TiffImagePlugin import IFDRational
|
|
|
10
10
|
from PIL.ExifTags import TAGS
|
|
11
11
|
import tifffile
|
|
12
12
|
from .. config.constants import constants
|
|
13
|
-
from .utils import write_img
|
|
13
|
+
from .utils import write_img, extension_jpg, extension_tif, extension_png
|
|
14
14
|
|
|
15
15
|
IMAGEWIDTH = 256
|
|
16
16
|
IMAGELENGTH = 257
|
|
@@ -48,11 +48,10 @@ def extract_enclosed_data_for_jpg(data, head, foot):
|
|
|
48
48
|
def get_exif(exif_filename):
|
|
49
49
|
if not os.path.isfile(exif_filename):
|
|
50
50
|
raise RuntimeError(f"File does not exist: {exif_filename}")
|
|
51
|
-
ext = exif_filename.split(".")[-1]
|
|
52
51
|
image = Image.open(exif_filename)
|
|
53
|
-
if
|
|
52
|
+
if extension_tif(exif_filename):
|
|
54
53
|
return image.tag_v2 if hasattr(image, 'tag_v2') else image.getexif()
|
|
55
|
-
if
|
|
54
|
+
if extension_jpg(exif_filename):
|
|
56
55
|
exif_data = image.getexif()
|
|
57
56
|
with open(exif_filename, 'rb') as f:
|
|
58
57
|
data = extract_enclosed_data_for_jpg(f.read(), b'<?xpacket', b'<?xpacket end="w"?>')
|
|
@@ -158,42 +157,40 @@ def write_image_with_exif_data(exif, image, out_filename, verbose=False):
|
|
|
158
157
|
if exif is None:
|
|
159
158
|
write_img(out_filename, image)
|
|
160
159
|
return None
|
|
161
|
-
ext = out_filename.split(".")[-1]
|
|
162
160
|
if verbose:
|
|
163
161
|
print_exif(exif)
|
|
164
|
-
if
|
|
162
|
+
if extension_jpg(out_filename):
|
|
165
163
|
cv2.imwrite(out_filename, image, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
|
|
166
164
|
add_exif_data_to_jpg_file(exif, out_filename, out_filename, verbose)
|
|
167
|
-
elif
|
|
165
|
+
elif extension_tif(out_filename):
|
|
168
166
|
metadata = {"description": f"image generated with {constants.APP_STRING} package"}
|
|
169
167
|
extra_tags, exif_tags = exif_extra_tags_for_tif(exif)
|
|
170
168
|
tifffile.imwrite(out_filename, image, metadata=metadata, compression='adobe_deflate',
|
|
171
169
|
extratags=extra_tags, **exif_tags)
|
|
172
|
-
elif
|
|
170
|
+
elif extension_png(out_filename):
|
|
173
171
|
image.save(out_filename, 'PNG', exif=exif, quality=100)
|
|
174
172
|
return exif
|
|
175
173
|
|
|
176
174
|
|
|
177
175
|
def save_exif_data(exif, in_filename, out_filename=None, verbose=False):
|
|
178
|
-
ext = in_filename.split(".")[-1]
|
|
179
176
|
if out_filename is None:
|
|
180
177
|
out_filename = in_filename
|
|
181
178
|
if exif is None:
|
|
182
179
|
raise RuntimeError('No exif data provided.')
|
|
183
180
|
if verbose:
|
|
184
181
|
print_exif(exif)
|
|
185
|
-
if
|
|
182
|
+
if extension_tif(in_filename):
|
|
186
183
|
image_new = tifffile.imread(in_filename)
|
|
187
184
|
else:
|
|
188
185
|
image_new = Image.open(in_filename)
|
|
189
|
-
if
|
|
186
|
+
if extension_jpg(in_filename):
|
|
190
187
|
add_exif_data_to_jpg_file(exif, in_filename, out_filename, verbose)
|
|
191
|
-
elif
|
|
188
|
+
elif extension_tif(in_filename):
|
|
192
189
|
metadata = {"description": f"image generated with {constants.APP_STRING} package"}
|
|
193
190
|
extra_tags, exif_tags = exif_extra_tags_for_tif(exif)
|
|
194
191
|
tifffile.imwrite(out_filename, image_new, metadata=metadata, compression='adobe_deflate',
|
|
195
192
|
extratags=extra_tags, **exif_tags)
|
|
196
|
-
elif
|
|
193
|
+
elif extension_png(in_filename):
|
|
197
194
|
image_new.save(out_filename, 'PNG', exif=exif, quality=100)
|
|
198
195
|
return exif
|
|
199
196
|
|
|
@@ -13,6 +13,7 @@ from .. config.constants import constants
|
|
|
13
13
|
from .. config.config import config
|
|
14
14
|
from .. core.colors import color_str
|
|
15
15
|
from .. core.framework import JobBase
|
|
16
|
+
from .utils import EXTENSIONS_TIF, EXTENSIONS_JPG, EXTENSIONS_PNG
|
|
16
17
|
from .stack_framework import FrameMultiDirectory
|
|
17
18
|
from .exif import exif_extra_tags_for_tif, get_exif
|
|
18
19
|
|
|
@@ -27,13 +28,13 @@ def write_multilayer_tiff(input_files, output_file, labels=None, exif_path='', c
|
|
|
27
28
|
msg = ", ".join(extensions)
|
|
28
29
|
raise RuntimeError("All input files must have the same extension. "
|
|
29
30
|
f"Input list has the following extensions: {msg}.")
|
|
30
|
-
extension = extensions[0]
|
|
31
|
-
if extension in
|
|
31
|
+
extension = extensions[0].lower()
|
|
32
|
+
if extension in EXTENSIONS_TIF:
|
|
32
33
|
images = [tifffile.imread(p) for p in input_files]
|
|
33
|
-
elif extension in
|
|
34
|
+
elif extension in EXTENSIONS_JPG:
|
|
34
35
|
images = [cv2.imread(p) for p in input_files]
|
|
35
36
|
images = [cv2.cvtColor(i, cv2.COLOR_BGR2RGB) for i in images]
|
|
36
|
-
elif extension
|
|
37
|
+
elif extension in EXTENSIONS_PNG:
|
|
37
38
|
images = [cv2.imread(p, cv2.IMREAD_UNCHANGED) for p in input_files]
|
|
38
39
|
images = [cv2.cvtColor(i, cv2.COLOR_BGR2RGB) for i in images]
|
|
39
40
|
if labels is None:
|
|
@@ -8,25 +8,65 @@ from .. config.config import config
|
|
|
8
8
|
from .. core.exceptions import ShapeError, BitDepthError
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
def get_path_extension(path):
|
|
12
|
+
return os.path.splitext(path)[1].lstrip('.')
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
EXTENSIONS_TIF = ['tif', 'tiff']
|
|
16
|
+
EXTENSIONS_JPG = ['jpg', 'jpeg']
|
|
17
|
+
EXTENSIONS_PNG = ['png']
|
|
18
|
+
EXTENSIONS_PDF = ['pdf']
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def extension_in(path, exts):
|
|
22
|
+
return get_path_extension(path).lower() in exts
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def extension_tif(path):
|
|
26
|
+
return extension_in(path, EXTENSIONS_TIF)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def extension_jpg(path):
|
|
30
|
+
return extension_in(path, EXTENSIONS_JPG)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def extension_png(path):
|
|
34
|
+
return extension_in(path, EXTENSIONS_PNG)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def extension_pdf(path):
|
|
38
|
+
return extension_in(path, EXTENSIONS_PDF)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def extension_tif_jpg(path):
|
|
42
|
+
return extension_in(path, EXTENSIONS_TIF + EXTENSIONS_JPG)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def extension_tif_png(path):
|
|
46
|
+
return extension_in(path, EXTENSIONS_TIF + EXTENSIONS_PNG)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def extension_jpg_png(path):
|
|
50
|
+
return extension_in(path, EXTENSIONS_JPG + EXTENSIONS_PNG)
|
|
51
|
+
|
|
52
|
+
|
|
11
53
|
def read_img(file_path):
|
|
12
54
|
if not os.path.isfile(file_path):
|
|
13
55
|
raise RuntimeError("File does not exist: " + file_path)
|
|
14
|
-
ext = file_path.split(".")[-1]
|
|
15
56
|
img = None
|
|
16
|
-
if
|
|
57
|
+
if extension_jpg(file_path):
|
|
17
58
|
img = cv2.imread(file_path)
|
|
18
|
-
elif
|
|
59
|
+
elif extension_tif_png(file_path):
|
|
19
60
|
img = cv2.imread(file_path, cv2.IMREAD_UNCHANGED)
|
|
20
61
|
return img
|
|
21
62
|
|
|
22
63
|
|
|
23
64
|
def write_img(file_path, img):
|
|
24
|
-
|
|
25
|
-
if ext in ['jpeg', 'jpg']:
|
|
65
|
+
if extension_jpg(file_path):
|
|
26
66
|
cv2.imwrite(file_path, img, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
|
|
27
|
-
elif
|
|
67
|
+
elif extension_tif(file_path):
|
|
28
68
|
cv2.imwrite(file_path, img, [int(cv2.IMWRITE_TIFF_COMPRESSION), 1])
|
|
29
|
-
elif
|
|
69
|
+
elif extension_png(file_path):
|
|
30
70
|
cv2.imwrite(file_path, img)
|
|
31
71
|
|
|
32
72
|
|
|
@@ -6,9 +6,10 @@ import argparse
|
|
|
6
6
|
import matplotlib
|
|
7
7
|
import matplotlib.backends.backend_pdf
|
|
8
8
|
matplotlib.use('agg')
|
|
9
|
-
from PySide6.QtWidgets import QApplication, QMainWindow, QStackedWidget,
|
|
9
|
+
from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QStackedWidget,
|
|
10
|
+
QMenu, QMessageBox, QDialog, QLabel, QListWidget, QPushButton)
|
|
10
11
|
from PySide6.QtGui import QAction, QIcon, QGuiApplication
|
|
11
|
-
from PySide6.QtCore import Qt, QEvent, QTimer
|
|
12
|
+
from PySide6.QtCore import Qt, QEvent, QTimer, Signal
|
|
12
13
|
from shinestacker.config.config import config
|
|
13
14
|
config.init(DISABLE_TQDM=True, COMBINED_APP=True, DONT_USE_NATIVE_MENU=True)
|
|
14
15
|
from shinestacker.config.constants import constants
|
|
@@ -20,6 +21,59 @@ from shinestacker.app.help_menu import add_help_action
|
|
|
20
21
|
from shinestacker.app.open_frames import open_frames
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
class SelectionDialog(QDialog):
|
|
25
|
+
selection_made = Signal(str, bool)
|
|
26
|
+
|
|
27
|
+
def __init__(self, title, message, items, parent=None):
|
|
28
|
+
super().__init__(parent)
|
|
29
|
+
self.setWindowTitle(title)
|
|
30
|
+
self.setModal(True)
|
|
31
|
+
self.selected_item = ""
|
|
32
|
+
self.setup_ui(message, items)
|
|
33
|
+
self.setMinimumSize(300, 300)
|
|
34
|
+
|
|
35
|
+
def setup_ui(self, message, items):
|
|
36
|
+
layout = QVBoxLayout(self)
|
|
37
|
+
if message:
|
|
38
|
+
label = QLabel(message)
|
|
39
|
+
layout.addWidget(label)
|
|
40
|
+
self.list_widget = QListWidget()
|
|
41
|
+
self.list_widget.addItems(items)
|
|
42
|
+
self.list_widget.itemSelectionChanged.connect(self.on_selection_changed)
|
|
43
|
+
layout.addWidget(self.list_widget)
|
|
44
|
+
button_layout = QHBoxLayout()
|
|
45
|
+
self.ok_button = QPushButton("OK")
|
|
46
|
+
self.ok_button.clicked.connect(self.accept)
|
|
47
|
+
self.ok_button.setEnabled(False)
|
|
48
|
+
button_layout.addWidget(self.ok_button)
|
|
49
|
+
cancel_button = QPushButton("Cancel")
|
|
50
|
+
cancel_button.clicked.connect(self.reject)
|
|
51
|
+
button_layout.addWidget(cancel_button)
|
|
52
|
+
layout.addLayout(button_layout)
|
|
53
|
+
|
|
54
|
+
def on_selection_changed(self):
|
|
55
|
+
selected_items = self.list_widget.selectedItems()
|
|
56
|
+
self.ok_button.setEnabled(len(selected_items) > 0)
|
|
57
|
+
|
|
58
|
+
def accept(self):
|
|
59
|
+
selected_items = self.list_widget.selectedItems()
|
|
60
|
+
if selected_items:
|
|
61
|
+
self.selected_item = selected_items[0].text()
|
|
62
|
+
self.selection_made.emit(self.selected_item, True)
|
|
63
|
+
super().accept()
|
|
64
|
+
|
|
65
|
+
def reject(self):
|
|
66
|
+
self.selected_item = ""
|
|
67
|
+
self.selection_made.emit("", False)
|
|
68
|
+
super().reject()
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def get_selection(title, message, items, parent=None):
|
|
72
|
+
dialog = SelectionDialog(title, message, items, parent)
|
|
73
|
+
result = dialog.exec()
|
|
74
|
+
return dialog.selected_item if result == QDialog.Accepted else ""
|
|
75
|
+
|
|
76
|
+
|
|
23
77
|
class MainApp(QMainWindow):
|
|
24
78
|
def __init__(self):
|
|
25
79
|
super().__init__()
|
|
@@ -41,6 +95,17 @@ class MainApp(QMainWindow):
|
|
|
41
95
|
self.retouch_window.menuBar().actions()[0], self.app_menu)
|
|
42
96
|
add_help_action(self.project_window)
|
|
43
97
|
add_help_action(self.retouch_window)
|
|
98
|
+
file_menu = None
|
|
99
|
+
for action in self.retouch_window.menuBar().actions():
|
|
100
|
+
if action.text() == "&File":
|
|
101
|
+
file_menu = action.menu()
|
|
102
|
+
break
|
|
103
|
+
if file_menu is not None:
|
|
104
|
+
import_action = QAction("Import From Current Project", self)
|
|
105
|
+
import_action.triggered.connect(self.import_from_project)
|
|
106
|
+
file_menu.addAction(import_action)
|
|
107
|
+
else:
|
|
108
|
+
raise RuntimeError("File menu not found!")
|
|
44
109
|
|
|
45
110
|
def switch_to_project(self):
|
|
46
111
|
self.switch_app(0)
|
|
@@ -91,6 +156,38 @@ class MainApp(QMainWindow):
|
|
|
91
156
|
else:
|
|
92
157
|
self.retouch_window.io_gui_handler.open_file(filename)
|
|
93
158
|
|
|
159
|
+
def import_from_project(self):
|
|
160
|
+
project = self.project_window.project()
|
|
161
|
+
if project is None:
|
|
162
|
+
QMessageBox.warning(self.parent(),
|
|
163
|
+
"No Active Project", "No project has been created or opened.")
|
|
164
|
+
return
|
|
165
|
+
if len(project.jobs) == 0:
|
|
166
|
+
QMessageBox.warning(self.parent(),
|
|
167
|
+
"No Jobs In Project", "The current project has no job. "
|
|
168
|
+
"Create and run a job first.")
|
|
169
|
+
return
|
|
170
|
+
if len(project.jobs) > 1:
|
|
171
|
+
job_names = [job.params['name'] for job in project.jobs]
|
|
172
|
+
job_name = SelectionDialog.get_selection(
|
|
173
|
+
"Job Selection",
|
|
174
|
+
"Please select one of the active jobs:",
|
|
175
|
+
job_names
|
|
176
|
+
)
|
|
177
|
+
job = None
|
|
178
|
+
for job in project.jobs:
|
|
179
|
+
if job.params['name'] == job_name:
|
|
180
|
+
break
|
|
181
|
+
if job is None:
|
|
182
|
+
return
|
|
183
|
+
else:
|
|
184
|
+
job = project.jobs[0]
|
|
185
|
+
retouch_path = self.project_window.get_retouch_path(job)
|
|
186
|
+
if isinstance(retouch_path, list):
|
|
187
|
+
open_frames(self.retouch_window, None, ";".join(retouch_path))
|
|
188
|
+
else:
|
|
189
|
+
self.retouch_window.io_gui_handler.open_file(retouch_path)
|
|
190
|
+
|
|
94
191
|
|
|
95
192
|
class Application(QApplication):
|
|
96
193
|
def event(self, event):
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, E0611, R0903, R0915, R0914, R0917, R0913, R0902
|
|
2
|
+
import os
|
|
2
3
|
from PySide6.QtWidgets import (QWidget, QPushButton, QVBoxLayout, QHBoxLayout,
|
|
3
4
|
QMessageBox, QScrollArea, QSizePolicy, QFrame, QLabel, QComboBox)
|
|
4
5
|
from PySide6.QtGui import QColor
|
|
@@ -7,6 +8,7 @@ from PySide6.QtCore import Signal, Slot
|
|
|
7
8
|
from .. config.constants import constants
|
|
8
9
|
from .. config.gui_constants import gui_constants
|
|
9
10
|
from .colors import RED_BUTTON_STYLE, BLUE_BUTTON_STYLE, BLUE_COMBO_STYLE
|
|
11
|
+
from .. algorithms.utils import extension_tif_jpg, extension_pdf
|
|
10
12
|
from .gui_logging import LogWorker, QTextEditLogger
|
|
11
13
|
from .gui_images import GuiPdfView, GuiImageView, GuiOpenApp
|
|
12
14
|
from .colors import (
|
|
@@ -200,13 +202,12 @@ class RunWindow(QTextEditLogger):
|
|
|
200
202
|
label = QLabel(name, self)
|
|
201
203
|
label.setStyleSheet("QLabel {margin-top: 5px; font-weight: bold;}")
|
|
202
204
|
self.image_layout.addWidget(label)
|
|
203
|
-
|
|
204
|
-
if ext == 'pdf':
|
|
205
|
+
if extension_pdf(path):
|
|
205
206
|
image_view = GuiPdfView(path, self)
|
|
206
|
-
elif
|
|
207
|
+
elif extension_tif_jpg(path):
|
|
207
208
|
image_view = GuiImageView(path, self)
|
|
208
209
|
else:
|
|
209
|
-
raise RuntimeError("Can't visualize file type {
|
|
210
|
+
raise RuntimeError(f"Can't visualize file type {os.path.splitext(path)[1]}.")
|
|
210
211
|
self.image_views.append(image_view)
|
|
211
212
|
self.image_layout.addWidget(image_view)
|
|
212
213
|
max_width = max(pv.size().width() for pv in self.image_views) if self.image_views else 0
|
|
@@ -7,13 +7,12 @@ from PySide6.QtGui import QIcon
|
|
|
7
7
|
from PySide6.QtCore import Qt
|
|
8
8
|
from .. config.gui_constants import gui_constants
|
|
9
9
|
from .. config.constants import constants
|
|
10
|
-
from .. algorithms.utils import read_img
|
|
10
|
+
from .. algorithms.utils import read_img, extension_tif_jpg
|
|
11
11
|
from .. algorithms.stack import get_bunches
|
|
12
12
|
from .select_path_widget import create_select_file_paths_widget
|
|
13
13
|
from .base_form_dialog import BaseFormDialog
|
|
14
14
|
|
|
15
15
|
DEFAULT_NO_COUNT_LABEL = " - "
|
|
16
|
-
EXTENSIONS = ['jpg', 'jpeg', 'tif', 'tiff']
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
class NewProjectDialog(BaseFormDialog):
|
|
@@ -133,10 +132,8 @@ class NewProjectDialog(BaseFormDialog):
|
|
|
133
132
|
return 0
|
|
134
133
|
count = 0
|
|
135
134
|
for filename in os.listdir(path):
|
|
136
|
-
if
|
|
137
|
-
|
|
138
|
-
if ext in EXTENSIONS:
|
|
139
|
-
count += 1
|
|
135
|
+
if extension_tif_jpg(filename):
|
|
136
|
+
count += 1
|
|
140
137
|
return count
|
|
141
138
|
|
|
142
139
|
self.n_image_files = count_image_files(self.input_folder.text())
|
|
@@ -173,11 +170,9 @@ class NewProjectDialog(BaseFormDialog):
|
|
|
173
170
|
file_path = None
|
|
174
171
|
for filename in files:
|
|
175
172
|
full_path = os.path.join(path, filename)
|
|
176
|
-
if
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
file_path = full_path
|
|
180
|
-
break
|
|
173
|
+
if extension_tif_jpg(full_path):
|
|
174
|
+
file_path = full_path
|
|
175
|
+
break
|
|
181
176
|
if file_path is None:
|
|
182
177
|
QMessageBox.warning(
|
|
183
178
|
self, "Invalid input", "Could not find images now in the selected path")
|
|
@@ -5,7 +5,7 @@ import numpy as np
|
|
|
5
5
|
import cv2
|
|
6
6
|
from psdtags import PsdChannelId
|
|
7
7
|
from PySide6.QtCore import QThread, Signal
|
|
8
|
-
from .. algorithms.utils import read_img
|
|
8
|
+
from .. algorithms.utils import read_img, extension_tif, extension_jpg
|
|
9
9
|
from .. algorithms.multilayer import read_multilayer_tiff
|
|
10
10
|
|
|
11
11
|
|
|
@@ -50,15 +50,14 @@ class FileLoader(QThread):
|
|
|
50
50
|
raise RuntimeError(f"Path {path} does not exist.")
|
|
51
51
|
if not os.path.isfile(path):
|
|
52
52
|
raise RuntimeError(f"Path {path} is not a file.")
|
|
53
|
-
|
|
54
|
-
if extension in ['jpg', 'jpeg']:
|
|
53
|
+
if extension_jpg(path):
|
|
55
54
|
try:
|
|
56
55
|
stack = np.array([cv2.cvtColor(read_img(path), cv2.COLOR_BGR2RGB)])
|
|
57
56
|
return stack, [path.split('/')[-1].split('.')[0]]
|
|
58
57
|
except Exception as e:
|
|
59
58
|
traceback.print_tb(e.__traceback__)
|
|
60
59
|
return None, None
|
|
61
|
-
elif
|
|
60
|
+
elif extension_tif(path):
|
|
62
61
|
try:
|
|
63
62
|
psd_data = read_multilayer_tiff(path)
|
|
64
63
|
layers = []
|
|
@@ -56,9 +56,7 @@ class IOGuiHandler(QObject, LayerCollectionHandler):
|
|
|
56
56
|
self.set_master_layer(master_layer)
|
|
57
57
|
self.undo_manager.reset()
|
|
58
58
|
self.blank_layer = np.zeros(master_layer.shape[:2])
|
|
59
|
-
self.finish_loading_setup(
|
|
60
|
-
stack, None, master_layer,
|
|
61
|
-
f"Loaded: {self.current_file_path()}")
|
|
59
|
+
self.finish_loading_setup(f"Loaded: {self.current_file_path()}")
|
|
62
60
|
|
|
63
61
|
def on_file_error(self, error_msg):
|
|
64
62
|
QApplication.restoreOverrideCursor()
|
|
@@ -151,11 +149,9 @@ class IOGuiHandler(QObject, LayerCollectionHandler):
|
|
|
151
149
|
for img, label in zip(stack, labels):
|
|
152
150
|
self.add_layer_label(label)
|
|
153
151
|
self.add_layer(img)
|
|
154
|
-
self.finish_loading_setup(
|
|
155
|
-
stack, labels, master,
|
|
156
|
-
"Selected frames imported")
|
|
152
|
+
self.finish_loading_setup("Selected frames imported")
|
|
157
153
|
|
|
158
|
-
def finish_loading_setup(self,
|
|
154
|
+
def finish_loading_setup(self, message):
|
|
159
155
|
self.display_manager.update_thumbnails()
|
|
160
156
|
self.mark_as_modified_requested.emit(True)
|
|
161
157
|
self.change_layer_requested.emit(0)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '1.0.3'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/base_stack_algo.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/noise_detection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/stack_framework.py
RENAMED
|
File without changes
|
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/algorithms/white_balance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/action_config_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/ico/focus_stack_bkg.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/img/close-round-line-icon.png
RENAMED
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/img/forward-button-icon.png
RENAMED
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/img/play-button-round-icon.png
RENAMED
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/gui/img/plus-round-line-icon.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/layer_collection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/unsharp_mask_filter.py
RENAMED
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/vignetting_filter.py
RENAMED
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker/retouch/white_balance_filter.py
RENAMED
|
File without changes
|
|
File without changes
|
{shinestacker-1.0.3 → shinestacker-1.0.4.post2}/src/shinestacker.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|