Skip to content

Commit

Permalink
get shit up to date etc
Browse files Browse the repository at this point in the history
  • Loading branch information
Eboreg committed Dec 2, 2024
1 parent 40366be commit 3204c67
Show file tree
Hide file tree
Showing 79 changed files with 33,633 additions and 983 deletions.
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ exclude =
__pycache__
*.pyi
**/lib
**/node_modules
ignore = W504,W601,F723,E731,E266,E741
max-line-length = 119
max-doc-length = 79
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ config.ini
defaults.conf
/image2ascii_db*
image2ascii/uploads/**
**/.mypy_cache

# Elastic Beanstalk Files
.elasticbeanstalk/*
Expand Down
2 changes: 0 additions & 2 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
graft image2ascii/templates
graft image2ascii/flags
graft image2ascii/static
2 changes: 1 addition & 1 deletion image2ascii/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = "0.6.3"
__version__ = "0.6.5"

VERSION = tuple(map(int, __version__.split(".")))
53 changes: 31 additions & 22 deletions image2ascii/core.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3

from typing import List, Optional, Tuple
from pathlib import Path

import numpy as np
from PIL import Image, ImageEnhance, ImageOps
Expand All @@ -23,15 +23,15 @@
class Image2ASCII(ConfigListener):
_config: Config

image: Optional[Image.Image] = None
output: Optional[Output] = None
image: Image.Image | None = None
output: Output | None = None

shapes: List[BaseShape]
shapes: list[BaseShape]

def __init__(
self,
file=None,
config: Optional[Config] = None,
config: Config | None = None,
):
if config is None:
config = Config.from_default_files()
Expand All @@ -49,14 +49,19 @@ def __hash__(self) -> int:
def config(self) -> Config:
return self._config

@property
def image_hash(self) -> int:
assert self.image
return hash(tuple(self.image.getdata()))

### CONVENIENCE SETTINGS METHODS ##########################################

def color_settings(
self,
color: Optional[bool] = None,
invert: Optional[bool] = None,
negative: Optional[bool] = None,
fill_all: Optional[bool] = None,
color: bool | None = None,
invert: bool | None = None,
negative: bool | None = None,
fill_all: bool | None = None,
):
if color is not None:
self.config.color = color
Expand All @@ -70,9 +75,9 @@ def color_settings(

def enhancement_settings(
self,
contrast: Optional[float] = None,
brightness: Optional[float] = None,
color_balance: Optional[float] = None
contrast: float | None = None,
brightness: float | None = None,
color_balance: float | None = None
):
if contrast is not None:
self.config.contrast = contrast
Expand All @@ -82,7 +87,7 @@ def enhancement_settings(
self.config.color_balance = color_balance
return self

def quality_settings(self, quality: Optional[int] = None, min_likeness: Optional[float] = None):
def quality_settings(self, quality: int | None = None, min_likeness: float | None = None):
if quality is not None:
self.config.quality = quality
if min_likeness is not None:
Expand All @@ -91,10 +96,10 @@ def quality_settings(self, quality: Optional[int] = None, min_likeness: Optional

def size_settings(
self,
max_height: Optional[int] = None,
width: Optional[int] = None,
ratio: Optional[float] = None,
crop: Optional[bool] = None
max_height: int | None = None,
width: int | None = None,
ratio: float | None = None,
crop: bool | None = None
):
"""
To explicitly set max height to None, if it has previously been set to
Expand All @@ -116,7 +121,7 @@ def config_changed(self, key, value):
self.reset()

@timer
def do_crop(self, image: Image.Image, matrix: np.ndarray) -> Tuple[Image.Image, bool]:
def do_crop(self, image: Image.Image, matrix: np.ndarray) -> tuple[Image.Image, bool]:
"""
`image` does not necessarily have the same dimensions as `matrix`, so
we transpose the cropbox before doing the actual cropping.
Expand Down Expand Up @@ -209,7 +214,7 @@ def do_resize(self, image: Image.Image) -> Image.Image:
return image

@timer
def get_char(self, nonzero_coords: List[Tuple[int, int]]) -> str:
def get_char(self, nonzero_coords: list[tuple[int, int]]) -> str:
chars = [] # list of (char, likeness) tuples

for shape in self.shapes:
Expand Down Expand Up @@ -292,7 +297,7 @@ def get_matrix(self, image: Image.Image):
return arr.reshape(image.height, image.width, 5) # pylint: disable=too-many-function-args

@timer
def get_section_color(self, section: np.ndarray, converter: Optional[BaseColorConverter]) -> Optional[np.ndarray]:
def get_section_color(self, section: np.ndarray, converter: BaseColorConverter | None) -> np.ndarray | None:
# Generate a 2-d array of colour data for points where V > 0:
if converter is not None:
colors = section[section[:, :, Vi] > 0]
Expand All @@ -301,7 +306,7 @@ def get_section_color(self, section: np.ndarray, converter: Optional[BaseColorCo
return converter.closest(color_arr)
return None

def get_section_size(self, image: Image.Image) -> Tuple[int, int]:
def get_section_size(self, image: Image.Image) -> tuple[int, int]:
section_width = int(image.width / self.config.width) or 1
return section_width, int(section_width * self.config.ratio) or 1

Expand Down Expand Up @@ -347,7 +352,7 @@ def load(self, file):
return self

@timer
def prepare_image(self) -> Tuple[Image.Image, np.ndarray]:
def prepare_image(self) -> tuple[Image.Image, np.ndarray]:
"""
There is a logic to the order of execution here; first of all, we need
a get_matrix() in order to do_crop(). That matrix has to be generated
Expand Down Expand Up @@ -423,3 +428,7 @@ def render(self):

def reset(self):
self.output = None

def save_image(self, filename: str | Path):
if self.image:
self.image.save(filename)
48 changes: 30 additions & 18 deletions image2ascii/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import shelve
from datetime import datetime, timedelta
from pathlib import Path
from typing import Dict, Optional, Union
from typing import Dict
from uuid import uuid4

from image2ascii.config import Config
Expand All @@ -11,32 +11,37 @@


class Session:
uuid: str

def __init__(
self,
uuid: str,
filename: Union[str, Path],
filename: str | Path,
config: Config,
keep_file=False,
hash: Optional[int] = None
hash: int | None = None,
flag: str | None = None,
uuid: str | None = None,
):
self.datetime = datetime.now()
self.uuid = uuid
self.uuid = uuid or str(uuid4())
self.filename = filename
self.config = config
self.keep_file = keep_file
self.hash = hash
self.flag = flag

@classmethod
def from_dict(cls, d: dict):
args = ["uuid", "filename", "config", "keep_file", "hash", "flag"]
return cls(**{k: v for k, v in d.items() if k in args})


class BaseDB:
def get(self, uuid: str) -> Session:
raise NotImplementedError

def get_by_hash(self, hash: int) -> Optional[Session]:
def get_by_hash(self, hash: int) -> Session | None:
raise NotImplementedError

def create(self, filename: Union[str, Path], config: Config, keep_file=False) -> Session:
def create(self, filename: str | Path, config: Config, keep_file=False) -> Session:
raise NotImplementedError

def update(self, uuid: str, config: Config) -> Session:
Expand All @@ -56,9 +61,10 @@ def __init__(self, filename="image2ascii_db"):
def get(self, uuid: str) -> Session:
"""May raise KeyError"""
with shelve.open(self.filename) as shelf:
return shelf["sessions"][uuid]
session = shelf["sessions"][uuid]
return Session.from_dict(session.__dict__)

def get_by_hash(self, hash: int) -> Optional[Session]:
def get_by_hash(self, hash: int) -> Session | None:
with shelve.open(self.filename) as shelf:
try:
return [s for s in shelf["sessions"].values() if s.hash == hash][0]
Expand All @@ -67,12 +73,13 @@ def get_by_hash(self, hash: int) -> Optional[Session]:

def create(
self,
filename: Union[str, Path],
filename: str | Path,
config: Config,
keep_file=False,
hash: Optional[int] = None,
hash: int | None = None,
flag: str | None = None,
) -> Session:
session = Session(str(uuid4()), filename, config, keep_file=keep_file, hash=hash)
session = Session(filename=filename, config=config, keep_file=keep_file, hash=hash, flag=flag)

with shelve.open(self.filename) as shelf:
sessions = shelf["sessions"]
Expand All @@ -82,16 +89,21 @@ def create(
self.purge()
return session

def update(self, uuid: str, config: Config) -> Session:
def update(self, uuid: str, config: Config, flag: str | None = None) -> Session:
with shelve.open(self.filename) as shelf:
sessions: Dict[str, Session] = shelf["sessions"]
# May raise KeyError:
session = sessions[uuid]
if session.config != config:
# If config changed, make a new session with new uuid
uuid = str(uuid4())
session = Session(uuid, session.filename, config, keep_file=session.keep_file, hash=session.hash)
sessions[uuid] = session
session = Session(
filename=session.filename,
config=config,
keep_file=session.keep_file,
hash=session.hash,
flag=flag,
)
sessions[session.uuid] = session
shelf["sessions"] = sessions
self.purge()
return session
Expand Down
Loading

0 comments on commit 3204c67

Please sign in to comment.