Skip to content

Commit

Permalink
Merge pull request #17 from TimNekk/develop (v1.4.0)
Browse files Browse the repository at this point in the history
Add Optimiztions
  • Loading branch information
TimNekk authored Jul 31, 2023
2 parents 389419c + 8f9b70c commit 3357716
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 30 deletions.
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

## Requirements

[Topaz Gigapixel AI](https://www.topazlabs.com/gigapixel-ai) **v6.3.3** or **newer** required
[Topaz Gigapixel AI](https://www.topazlabs.com/gigapixel-ai) **v6.1.0** or **newer** required

## Installation

Expand Down Expand Up @@ -55,12 +55,25 @@ app = Gigapixel(exe_path, output_suffix)

# Process image.
image = Path('path/to/image.jpg')
output_path = app.process(image, scale=Scale.X2, mode=Mode.STANDARD, delete_from_history=True, output_format=OutputFormat.PNG)
output_path = app.process(image)

# Print output path.
print(output_path)
```

Additional parameters can be passed to `process()` method **(Takes additional time)**:
```python
from gigapixel import Scale, Mode, OutputFormat

output_path = app.process(image, scale=Scale.X2, mode=Mode.STANDARD, delete_from_history=True, output_format=OutputFormat.PNG)
```

> **Warning!**
> Using parameters (`scale`, `mode`, `output_format`, `delete_from_history`) will take **additional time** to process single image.
> Consider using them only when needed.
> To get the best performance, use `app.process(image)`

## Contributing

Bug reports and/or pull requests are welcome
Expand Down
100 changes: 72 additions & 28 deletions gigapixel/gigapixel.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import os
from enum import Enum
from typing import Optional
from typing import Optional, Dict, Any
from pathlib import Path

from .logging import log, Level
from .exceptions import NotFile, FileAlreadyExists, ElementNotFound

from pywinauto import ElementNotFoundError
from pywinauto import ElementNotFoundError, timings
import clipboard
from loguru import logger
from pywinauto.application import Application, ProcessNotFoundError
Expand Down Expand Up @@ -47,16 +47,31 @@ def __init__(self,
self._output_suffix = output_suffix

instance = self._get_gigapixel_instance()
self._app = self.App(instance)
self._app = self._App(instance)

class App:
class _App:
def __init__(self, app: Application):
self._app = app
self._main_window = self._app.window()
timings.Timings.window_find_timeout = 0.5

self.app = app
self._main_window = self.app.window()

self.scale: Optional[Scale] = None
self.mode: Optional[Mode] = None

self._cancel_processing_button: Optional[Any] = None
self._delete_button: Optional[Any] = None
self._output_combo_box: Optional[Any] = None
self._preserve_source_format_button: Optional[Any] = None
self._jpg_button: Optional[Any] = None
self._jpeg_button: Optional[Any] = None
self._tif_button: Optional[Any] = None
self._tiff_button: Optional[Any] = None
self._png_button: Optional[Any] = None
self._dng_button: Optional[Any] = None
self._scale_buttons: Dict[Scale, Any] = {}
self._mode_buttons: Dict[Mode, Any] = {}

@log("Opening photo: {}", "Photo opened", format=(1,), level=Level.DEBUG)
def open_photo(self, photo_path: Path) -> None:
while photo_path.name not in self._main_window.element_info.name:
Expand All @@ -67,16 +82,24 @@ def open_photo(self, photo_path: Path) -> None:
send_keys('^v {ENTER}')

@log("Saving photo", "Photo saved", level=Level.DEBUG)
def save_photo(self, output_format: OutputFormat) -> None:
def save_photo(self, output_format: Optional[OutputFormat]) -> None:
send_keys('^S')
self._set_output_format(output_format)

if output_format:
self._set_output_format(output_format)

send_keys('{ENTER}')
self._main_window.child_window(title="Cancel Processing", control_type="Button").wait_not('visible',
timeout=60)
if self._cancel_processing_button is None:
self._cancel_processing_button = self._main_window.child_window(title="Cancel Processing",
control_type="Button",
depth=1)
self._cancel_processing_button.wait_not('visible', timeout=60)

@log("Deleting photo from history", "Photo deleted", level=Level.DEBUG)
def delete_photo(self) -> None:
self._main_window.Pane.Button2.click_input()
if self._delete_button is None:
self._delete_button = self._main_window.Pane.Button2
self._delete_button.click_input()

@log("Setting processing options", "Processing options set", level=Level.DEBUG)
def set_processing_options(self, scale: Optional[Scale] = None, mode: Optional[Mode] = None) -> None:
Expand All @@ -86,36 +109,54 @@ def set_processing_options(self, scale: Optional[Scale] = None, mode: Optional[M
self._set_mode(mode)

def _set_output_format(self, save_format: OutputFormat) -> None:
self._main_window.ComboBox.click_input()
if self._output_combo_box is None:
self._output_combo_box = self._main_window.ComboBox
self._output_combo_box.click_input()

if save_format == OutputFormat.PRESERVE_SOURCE_FORMAT:
self._main_window.ListItem.click_input()
if self._preserve_source_format_button is None:
self._preserve_source_format_button = self._main_window.ListItem
self._preserve_source_format_button.click_input()
send_keys('{TAB}')
elif save_format == OutputFormat.JPG:
self._main_window.ListItem2.click_input()
if self._jpg_button is None:
self._jpg_button = self._main_window.ListItem2
self._jpg_button.click_input()
send_keys('{TAB}')
elif save_format == OutputFormat.JPEG:
self._main_window.ListItem3.click_input()
if self._jpeg_button is None:
self._jpeg_button = self._main_window.ListItem3
self._jpeg_button.click_input()
send_keys('{TAB}')
elif save_format == OutputFormat.TIF:
self._main_window.ListItem4.click_input()
if self._tif_button is None:
self._tif_button = self._main_window.ListItem4
self._tif_button.click_input()
send_keys('{TAB} {TAB} {TAB}')
elif save_format == OutputFormat.TIFF:
self._main_window.ListItem5.click_input()
if self._tiff_button is None:
self._tiff_button = self._main_window.ListItem5
self._tiff_button.click_input()
send_keys('{TAB} {TAB} {TAB}')
elif save_format == OutputFormat.PNG:
self._main_window.ListItem6.click_input()
if self._png_button is None:
self._png_button = self._main_window.ListItem6
self._png_button.click_input()
send_keys('{TAB}')
elif save_format == OutputFormat.DNG:
self._main_window.ListItem7.click_input()
if self._dng_button is None:
self._dng_button = self._main_window.ListItem7
self._dng_button.click_input()
send_keys('{TAB}')

def _set_scale(self, scale: Scale):
if self.scale == scale:
return

try:
self._main_window.child_window(title=scale.value).click_input()
if scale not in self._scale_buttons:
self._scale_buttons[scale] = self._main_window.child_window(title=scale.value)
self._scale_buttons[scale].click_input()
except ElementNotFoundError:
raise ElementNotFound(f"Scale button {scale.value} not found")
self.scale = scale
Expand All @@ -126,7 +167,9 @@ def _set_mode(self, mode: Mode) -> None:
return

try:
self._main_window.child_window(title=mode.value).click_input()
if mode not in self._mode_buttons:
self._mode_buttons[mode] = self._main_window.child_window(title=mode.value)
self._mode_buttons[mode].click_input()
except ElementNotFoundError:
raise ElementNotFound(f"Mode button {mode.value} not found")
self.mode = mode
Expand All @@ -152,7 +195,7 @@ def _open_topaz(self) -> Application:
return instance

@log("Checking path: {}", "Path is valid", format=(1,), level=Level.DEBUG)
def _check_path(self, path: Path, output_format: OutputFormat) -> None:
def _check_path(self, path: Path, output_format: Optional[OutputFormat]) -> None:
if not path.is_file():
raise NotFile(f"Path is not a file: {path}")

Expand All @@ -166,18 +209,19 @@ def _remove_suffix(input_string: str, suffix: str) -> str:
return input_string[:-len(suffix)]
return input_string

def _get_save_path(self, path: Path, output_format: OutputFormat) -> Path:
extension = path.suffix if output_format == OutputFormat.PRESERVE_SOURCE_FORMAT else f".{output_format.value.lower()}"
def _get_save_path(self, path: Path, output_format: Optional[OutputFormat]) -> Path:
extension = path.suffix if output_format is None or output_format == OutputFormat.PRESERVE_SOURCE_FORMAT else \
f".{output_format.value.lower()}"
return path.parent / (Gigapixel._remove_suffix(path.name, path.suffix) + self._output_suffix + extension)

@log(start="Starting processing: {}", format=(1,))
@log(end="Finished processing: {}", format=(1,), level=Level.SUCCESS)
def process(self,
photo_path: Path,
scale: Scale = Scale.X2,
mode: Mode = Mode.STANDARD,
delete_from_history: bool = True,
output_format: OutputFormat = OutputFormat.PRESERVE_SOURCE_FORMAT
scale: Optional[Scale] = None,
mode: Optional[Mode] = None,
delete_from_history: bool = False,
output_format: Optional[OutputFormat] = None
) -> Path:
self._check_path(photo_path, output_format)

Expand Down

0 comments on commit 3357716

Please sign in to comment.