Skip to content

Commit

Permalink
Merge pull request #19 from MechTechnology/dev
Browse files Browse the repository at this point in the history
Updates main to V3.1 - Community Patch
  • Loading branch information
MechTechnology authored Mar 31, 2023
2 parents fc3b750 + 2d72b98 commit f2ca544
Show file tree
Hide file tree
Showing 18 changed files with 537 additions and 252 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pillow = "*"
numpy = "*"
pyside6 = "*"
pyqtdarktheme = "*"
psd-tools = "*"

[dev-packages]
flake8 = "*"
Expand Down
658 changes: 456 additions & 202 deletions Pipfile.lock

Large diffs are not rendered by default.

34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center">
<a href="https://github.com/MechTechnology/SmartStitch">
<img alt="SmartStitch.Logo" width="200" heigth="200" src="https://github.com/MechTechnology/SmartStitch/raw/dev/assets/SmartStitchLogo.png">
<img alt="SmartStitch.Logo" width="200" height="200" src="https://github.com/MechTechnology/SmartStitch/raw/dev/assets/SmartStitchLogo.png">
</a>
<h1>SmartStitch</h1>
<p>
Expand Down Expand Up @@ -46,7 +46,7 @@ The smart part of the name comes from the fact that it uses some simple pixel ca
## Basic Quick Get Started GUI Version
1. Open the application.
2. Browse to your raw folder.
4. Select a the output file type. (Supported types: png, jpg, webp, bmp, tiff, tga)
4. Select a the output file type. (Supported types: png, jpg, webp, bmp, psd, tiff, tga)
3. Set the Rough Panel Height of the output files.
5. Click start process.
6. Done, Enjoy!
Expand All @@ -71,7 +71,7 @@ The smart part of the name comes from the fact that it uses some simple pixel ca
Keep in mind that this setup is only needed once, after running the setup.py, you can just launch ```SmartStitchGUI.py``` directly every time

## Reporting Bugs [New to 3.0+]:
A very robust logging system has been implemented in the GUI version of SmartStitch for almost every interaction with the program, when an error occur the application will inform you about it, and leaves the details in a file called in the ```__logs__``` folder, There will be a file created for every day of usage. you can open an issue ticket here and attach the file, so it can be easily debugged and fixed.
A very robust logging system has been implemented in the GUI version of SmartStitch for almost every interaction with the program, when an error occur the application will inform you about it, and leaves the details in a file called in the ```__logs__``` folder, There will be a file created for every day of usage. you can open an issue ticket here and attach the file, so it can be easily debugged and fixed.

And since it's just one person maintaining this application, only accepted tickets will be for version 3.0 and above. Please don't open tickets for lower versions, since your problem could have been already solved.

Expand All @@ -83,17 +83,17 @@ You can also contact me at Discord if you don't want to use the GitHub Issue Sys
Here is the complete documentation for the application, it is broken down into 4 sections, basic settings, advanced settings, how to build your own version, how to run the console version.

## Basic Settings
These are the required settings that all users should be mindful of.
These are the required settings that all users should be mindful of.

### Input Folder Path
Here you have to set the path for the Input Folder which contains the raws that will be processed by the program. If batch mode is enabled, it will search for subfolder within the given input path. So make sure your folder and files are in order.

*Console Parameter Name: --input_folder, -i*

### Output type
The default output type is png since it is lossless, however you can always change to other types, such as jpg, the program does save jpg at 100 quality, so there should be not noticable loss in quality but it is up to the user what format they want.
The default output type is png since it is lossless, however you can always change to other types, such as jpg, the program does save jpg at 100 quality, so there should be not noticeable loss in quality but it is up to the user what format they want. (You can also now use PSD files for convenience if you are a Photoshop user, however output files will not contain the layers of the original input psd file)

*Default: .png* --- *Supported Types: png, jpg, webp, bmp, tiff, tga* --- *Console Parameter Name: -t*
*Default: .png* --- *Supported Types: png, jpg, webp, bmp, psd, tiff, tga* --- *Console Parameter Name: -t*

### Rough Output Height
Here you set the size that you want most output panels to roughly be, the program will uses it as a guide to see where to slice/cut the images, however it IS ROUGH, meaning if the program finds bubbles/sfx/whatever at that specific pixel length, it will try to find the next closest position where it can cut the image. Thus the output size of each image will vary because of that, but they all will be roughly around this size.
Expand Down Expand Up @@ -135,7 +135,7 @@ This is the step at which the program moves if it find the line it's on to be un

*Default: 5* --- *Value Range: 1-100* --- *Console Parameter Name: -sl*

### Ignorable Horizental Margins Pixels
### Ignorable Horizental Margins Pixels
This gives the option to ignore pixels on the border of the image when checking for bubbles/sfw/whatever. Why you might ask, Borders do not make the detection algorithm happy, so in some cases you want it to start its detection only inside said border, be careful to what value you want it to be since if it's larger that image it will case the program to crash/stop its operation.

*Default: 0* --- *Console Parameter Name: -ip*
Expand Down Expand Up @@ -174,22 +174,22 @@ Of course you can use whatever version of waifu2x or process that you want, this

### Console Version Usage
```
python SmartStitchConsole.py [-h] -i INPUT_FOLDER
-sh SPLIT_HEIGHT
[-t {.png,.jpg,.webp,.bmp,.tiff,.tga}]
[-cw CUSTOM_WIDTH]
[-dt {none,pixel}]
[-s [0-100]]
[-lq [1-100]]
[-ip IGNORABLE_PIXELS]
python SmartStitchConsole.py [-h] -i INPUT_FOLDER
-sh SPLIT_HEIGHT
[-t {.png,.jpg,.webp,.bmp,.psd,.tiff,.tga}]
[-cw CUSTOM_WIDTH]
[-dt {none,pixel}]
[-s [0-100]]
[-lq [1-100]]
[-ip IGNORABLE_PIXELS]
[-sl [1-100]]
required arguments:
--input_folder INPUT_FOLDER, -i INPUT_FOLDER Sets the path of Input Folder
optional arguments:
-h, --help show this help message and exit
-i INPUT_FOLDER Sets the path of Input Folder
-sh SPLIT_HEIGHT Sets the value of the Rough Panel Height
-t {.png,.jpg,.webp,.bmp,.tiff,.tga}
-t {.png,.jpg,.webp,.bmp,.psd,.tiff,.tga}
Sets the type/format of the Output Image Files
-cw CUSTOM_WIDTH [Advanced] Forces Fixed Width for All Output Image Files, Default=None (Disabled)
-dt {none,pixel} [Advanced] Sets the type of Slice Location Detection, Default=pixel (Pixel Comparison)
Expand All @@ -202,7 +202,7 @@ optional arguments:
### Console Version Command Example
```
python SmartStitchConsole.py -i "Review me" -sh 7500 -t ".png"
# This will Run the application on for input_folder of "./Review me" with split_height of 7500 and output_tyoe of ".png"
# This will Run the application on for input_folder of "./Review me" with split_height of 7500 and output_type of ".png"
```

## How to build/compile your own GUI Version?
Expand Down
10 changes: 5 additions & 5 deletions console/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def launch():
parser.add_argument(
"-sh",
dest='split_height',
type=postive_int,
type=positive_int,
required=True,
help='Sets the value of the Rough Panel Height',
)
Expand All @@ -24,13 +24,13 @@ def launch():
dest='output_type',
type=str,
default=".png",
choices=['.png', '.jpg', '.webp', '.bmp', '.tiff', '.tga'],
choices=['.png', '.jpg', '.webp', '.bmp', '.tiff', '.tga', '.psd'],
help='Sets the type/format of the Output Image Files',
)
parser.add_argument(
"-cw",
dest='custom_width',
type=postive_int,
type=positive_int,
default=-1,
help='[Advanced] Forces Fixed Width for All Output Image Files, Default=None (Disabled)',
)
Expand Down Expand Up @@ -63,7 +63,7 @@ def launch():
parser.add_argument(
"-ip",
dest='ignorable_pixels',
type=postive_int,
type=positive_int,
default=5,
help='[Advanced] Sets the value of Ignorable Border Pixels, Default=5 (5px)',
)
Expand All @@ -81,7 +81,7 @@ def launch():
process.run(kwargs)


def postive_int(value):
def positive_int(value):
ivalue = int(value)
if ivalue <= 0:
raise argparse.ArgumentTypeError("%s is an invalid positive int value" % value)
Expand Down
4 changes: 2 additions & 2 deletions core/detectors/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .direct_slicing import DirectSlicingDetector
from .pixel_comparsion import PixelComparsionDetector
from .pixel_comparison import PixelComparisonDetector
from .selector import select_detector

__all__ = [DirectSlicingDetector, PixelComparsionDetector, select_detector]
__all__ = [DirectSlicingDetector, PixelComparisonDetector, select_detector]
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
from core.services.global_logger import logFunc


class PixelComparsionDetector:
class PixelComparisonDetector:
@logFunc(inclass=True)
def run(self, combined_img: pil.Image, split_height: int, **kwargs) -> list[int]:
"""Uses Neighbouring pixels comparison to detect ideal slice locations"""
# Changes from a pil Image to an numpy pixel array
combined_img = np.array(combined_img.convert('L'))
# Setting up rest of Detector Paramaters
# Setting up rest of Detector Parameters
scan_step = kwargs.get('scan_step', 5)
ignorable_pixels = kwargs.get('ignorable_pixels', 0)
sensitivity = kwargs.get('sensitivity', 90)
Expand Down
6 changes: 3 additions & 3 deletions core/detectors/selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from ..services import logFunc
from .direct_slicing import DirectSlicingDetector
from .pixel_comparsion import PixelComparsionDetector
from .pixel_comparison import PixelComparisonDetector


@logFunc()
Expand All @@ -11,8 +11,8 @@ def select_detector(detection_type: str | DETECTION_TYPE):
return DirectSlicingDetector()
elif (
detection_type == "pixel"
or detection_type == DETECTION_TYPE.PIXEL_COMPARSION.value
or detection_type == DETECTION_TYPE.PIXEL_COMPARISON.value
):
return PixelComparsionDetector()
return PixelComparisonDetector()
else:
raise Exception("Invalid Detection Type")
2 changes: 1 addition & 1 deletion core/models/app_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def __init__(self, json_dict: dict[str, any] = None):
self.split_height: int = 5000
self.output_type: str = '.png'
self.lossy_quality: str = 100
self.detector_type: DETECTION_TYPE = DETECTION_TYPE.PIXEL_COMPARSION
self.detector_type: DETECTION_TYPE = DETECTION_TYPE.PIXEL_COMPARISON
self.senstivity: int = 90
self.ignorable_pixels: int = 5
self.scan_step: int = 5
Expand Down
4 changes: 2 additions & 2 deletions core/services/directory_explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from natsort import natsorted

from ..models import WorkDirectory
from ..utils.constants import OUTPUT_SUFFIX, POSTPROCESS_SUFFIX, SUPPORTTED_IMG_TYPES
from ..utils.constants import OUTPUT_SUFFIX, POSTPROCESS_SUFFIX, SUPPORTED_IMG_TYPES
from ..utils.errors import DirectoryException
from .global_logger import logFunc

Expand Down Expand Up @@ -33,7 +33,7 @@ def explore_directories(self, main_directory: WorkDirectory) -> list[WorkDirecto
):
img_files = []
for file in files:
if file.lower().endswith(SUPPORTTED_IMG_TYPES):
if file.lower().endswith(SUPPORTED_IMG_TYPES):
img_files.append(file)
img_files = natsorted(img_files)
if img_files:
Expand Down
4 changes: 2 additions & 2 deletions core/services/global_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class GlobalLogger:
@classmethod
def configureGlobalLogger(self):
"""Initalizes and Configures Logging Service"""
"""Initializes and Configures Logging Service"""
if not os.path.exists(LOG_REL_DIR):
os.makedirs(LOG_REL_DIR)
current_date = datetime.now()
Expand All @@ -19,7 +19,7 @@ def configureGlobalLogger(self):
log_level = logging.DEBUG
log_format = '%(levelname)s:%(asctime)s:%(message)s'
logging.basicConfig(format=log_format, filename=log_filename, level=log_level)
logging.debug('GlobalLogger:Logger Initalized')
logging.debug('GlobalLogger:Logger Initialized')
# Removes the pil logging from polluting the Debug Level.
pil_logger = logging.getLogger('PIL')
pil_logger.setLevel(logging.INFO)
Expand Down
2 changes: 1 addition & 1 deletion core/services/global_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,6 @@ def update(self, message: str = None, fraction: float = 1):
self.progress_track += value
percentage = float(self.progress_track / self.total_progress) * 100
if not message:
message = func_name + ' ran sucessfully!'
message = func_name + ' ran successfully!'
for subscriber in self.subscribers:
subscriber(percentage, message)
23 changes: 17 additions & 6 deletions core/services/image_handler.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import os

from PIL import Image as pil
from psd_tools import PSDImage

from ..models import WorkDirectory
from .global_logger import logFunc
from ..utils.constants import PHOTOSHOP_FILE_TYPES


class ImageHandler:
Expand All @@ -13,7 +15,10 @@ def load(self, workdirectory: WorkDirectory) -> list[pil.Image]:
img_objs = []
for imgFile in workdirectory.input_files:
imgPath = os.path.join(workdirectory.input_path, imgFile)
image = pil.open(imgPath)
if os.path.splitext(imgPath)[1] not in PHOTOSHOP_FILE_TYPES:
image = pil.open(imgPath)
else:
image = PSDImage.open(imgPath).topil()
img_objs.append(image)
return img_objs

Expand All @@ -29,11 +34,17 @@ def save(
if not os.path.exists(workdirectory.output_path):
os.makedirs(workdirectory.output_path)
img_file_name = str(f'{img_iteration:02}') + img_format
img_obj.save(
workdirectory.output_path + '/' + img_file_name,
quality=quality,
)
img_obj.close()
if img_format in PHOTOSHOP_FILE_TYPES:
psd_obj = PSDImage.frompil(img_obj)
psd_obj.save(
workdirectory.output_path + '/' + img_file_name,
)
else:
img_obj.save(
workdirectory.output_path + '/' + img_file_name,
quality=quality,
)
img_obj.close()
workdirectory.output_files.append(img_file_name)
return img_file_name

Expand Down
4 changes: 2 additions & 2 deletions core/services/image_manipulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ def slice(
for index in range(1, len(slice_locations)):
upper_limit = slice_locations[index - 1]
lower_limit = slice_locations[index]
slice_bounderies = (0, upper_limit, max_width, lower_limit)
img_slice = combined_img.crop(slice_bounderies)
slice_boundaries = (0, upper_limit, max_width, lower_limit)
img_slice = combined_img.crop(slice_boundaries)
img_objs.append(img_slice)
combined_img.close()
return img_objs
10 changes: 8 additions & 2 deletions core/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
SETTINGS_REL_DIR = '__settings__'
OUTPUT_SUFFIX = ' [stitched]'
POSTPROCESS_SUFFIX = ' [processed]'
SUPPORTTED_IMG_TYPES = (
SUPPORTED_IMG_TYPES = (
'.png',
'.webp',
'.jpg',
Expand All @@ -14,8 +14,14 @@
'.bmp',
'.tiff',
'.tga',
'.psd',
'.psb',
)

PHOTOSHOP_FILE_TYPES = (
".psd",
".psb"
)

# Static Enums
class WIDTH_ENFORCEMENT(IntEnum):
Expand All @@ -26,4 +32,4 @@ class WIDTH_ENFORCEMENT(IntEnum):

class DETECTION_TYPE(IntEnum):
NO_DETECTION = 0
PIXEL_COMPARSION = 1
PIXEL_COMPARISON = 1
4 changes: 2 additions & 2 deletions gui/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def run(self):
)


def initalize_gui():
def initialize_gui():
global MainWindow
global settings
global appVersion
Expand All @@ -44,7 +44,7 @@ def initalize_gui():
appIcon = QIcon(pixmap)
MainWindow.setWindowIcon(appIcon)
# Sets Window Title
appVersion = "3.0"
appVersion = "3.1"
appAuthor = "MechTechnology"
MainWindow.setWindowTitle("SmartStitch By {0} [{1}]".format(appAuthor, appVersion))
# Controls Setup
Expand Down
4 changes: 2 additions & 2 deletions gui/launcher.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication

from .controller import initalize_gui
from .controller import initialize_gui
from .stylesheet import load_styling


def launch():
app = QApplication([])
app.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps)
initalize_gui()
initialize_gui()
app.setStyleSheet(load_styling())
app.exec()
7 changes: 6 additions & 1 deletion gui/layout.ui
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@
<string>.bmp</string>
</property>
</item>
<item>
<property name="text">
<string>.psd</string>
</property>
</item>
<item>
<property name="text">
<string>.tiff</string>
Expand Down Expand Up @@ -429,7 +434,7 @@
</item>
<item>
<property name="text">
<string>Smart Pixel Comparsion</string>
<string>Smart Pixel Comparison</string>
</property>
</item>
</widget>
Expand Down
Loading

0 comments on commit f2ca544

Please sign in to comment.