Skip to content

Commit a5d9067

Browse files
authored
Merge pull request #42 from huideyeren/v2
v2
2 parents d2293fe + 80038b4 commit a5d9067

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1235
-553
lines changed

.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ exclude =
77
old,
88
build,
99
dist
10-
ignore = E501
10+
ignore = E501, W503, F841

.github/workflows/testing.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ jobs:
1111
strategy:
1212
fail-fast: false
1313
matrix:
14-
python-version: ["3.10", "3.11", "3.12"]
15-
poetry-version: [1.2.2]
14+
python-version: ["3.10", "3.11", "3.12", "3.13"]
15+
poetry-version: [1.8.2]
1616
os: [ubuntu-latest, macos-latest, windows-latest]
1717
runs-on: ${{ matrix.os }}
1818
steps:
@@ -27,9 +27,9 @@ jobs:
2727
- name: Install Dependencies
2828
run: poetry install --no-interaction
2929
- name: Format Check
30-
run: poetry run flake8 src/ --ignore=E501,E203,W503,W504
31-
- name: Type Check
32-
run: poetry run mypy src/
30+
run: poetry run flake8 src/ --ignore=E501,E203,W503,W504,F841
31+
# - name: Type Check
32+
# run: poetry run mypy src/
3333
- name: Run Tests
3434
run: poetry run pytest -v tests/ --cov-config=.coveragerc --cov=src/ --cov-branch --cov-report=xml
3535
- name: Upload coverage to Codecov

pyproject.toml

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "anshitsu"
3-
version = "1.5.1" # using poetry-dynamic-versioning
3+
version = "2.0.2" # using poetry-dynamic-versioning
44
description = "A tiny digital photographic utility."
55
readme = "README.md"
66
authors = ["Iosif Takakura <[email protected]>"]
@@ -14,30 +14,30 @@ enable = true
1414
style = "pep440"
1515

1616
[tool.poetry.dependencies]
17-
python = "<3.13,>=3.10"
18-
numpy = "^1.26.0"
19-
Pillow = "^10.1.0"
20-
colorcorrect = "^0.9.1"
21-
fire = "^0.5.0"
17+
python = "<4.0,>=3.10"
18+
numpy = "*"
19+
Pillow = "*"
20+
colorcorrect = "*"
21+
fire = "*"
2222

2323
[tool.poetry.dev-dependencies]
24-
pytest = "^6.2"
25-
flake8 = "^6.1.0"
26-
mypy = "^0.981"
27-
black = "^23.10"
28-
isort = "^5.12.0"
29-
pyproject-flake8 = "^0.0.1a4"
30-
pytest-randomly = "^3.15.0"
31-
tox = "^4.11.3"
32-
pytest-cov = "^4.1.0"
24+
pytest = "*"
25+
flake8 = "*"
26+
mypy = "*"
27+
black = "*"
28+
isort = "*"
29+
pyproject-flake8 = "*"
30+
pytest-randomly = "*"
31+
tox = "*"
32+
pytest-cov = "*"
3333

3434
[tool.poetry.scripts]
35-
anshitsu = "anshitsu.cli:main"
35+
anshitsu = "anshitsu.main:main"
3636

3737
[tool.poetry.group.dev.dependencies]
38-
importlib-metadata = "^6.8.0"
39-
poetry-dynamic-versioning = "^1.1.1"
40-
setuptools = "^68.2.2"
38+
importlib-metadata = "*"
39+
poetry-dynamic-versioning = "*"
40+
setuptools = "*"
4141

4242
[build-system]
4343
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]

src/anshitsu/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version: str = "v1.5.1"
1+
version: str = "v2.0.2"

src/anshitsu/cli.py

Lines changed: 154 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,162 @@
1+
import datetime
2+
import glob
3+
import os
4+
import os.path
5+
import re
6+
from typing import Optional
7+
18
import fire
9+
import fire.core
10+
from PIL import Image, UnidentifiedImageError
211

3-
from anshitsu.process import process
12+
from anshitsu.__version__ import version as __version__
13+
from anshitsu.process.processor import Processor
414

515

6-
def main():
16+
def cli(
17+
path: Optional[str] = None,
18+
colorautoadjust: bool = False,
19+
colorstretch: bool = False,
20+
grayscale: bool = False,
21+
invert: bool = False,
22+
color: Optional[float] = None,
23+
brightness: Optional[float] = None,
24+
sharpness: Optional[float] = None,
25+
contrast: Optional[float] = None,
26+
tosaka: Optional[float] = None,
27+
outputrgb: bool = False,
28+
sepia: bool = False,
29+
cyanotype: bool = False,
30+
noise: Optional[float] = None,
31+
overwrite: bool = False,
32+
version: bool = False,
33+
line_drawing: bool = False,
34+
posterize: Optional[int] = None,
35+
) -> str:
736
"""
8-
main [summary]
37+
Process Runnner for Command Line Interface
38+
39+
This utility converts the colors of images such as photos.
40+
41+
If you specify a directory path, it will convert
42+
the image files in the specified directory.
43+
If you specify a file path, it will convert the specified file.
44+
If you specify an option, the specified conversion will be performed.
45+
46+
Tosaka mode is a mode that expresses the preference of
47+
Tosaka-senpai, a character in "Kyūkyoku Chōjin R",
48+
for "photos taken with Tri-X that look like they were
49+
burned onto No. 4 or No. 5 photographic paper".
50+
Only use floating-point numbers when using this mode;
51+
numbers around 2.4 will make it look right.
52+
53+
Args:
54+
path (Optional[str], optional): Directory or File Path. Defaults to None.
55+
overwrite (bool, optional): Overwrite original files. Defaults to False.
56+
colorautoadjust (bool, optional): Use colorautoadjust algorithm. Defaults to False.
57+
colorstretch (bool, optional): Use colorstretch algorithm. Defaults to False.
58+
grayscale (bool, optional): Convert to grayscale. Defaults to False.
59+
invert (bool, optional): Invert color. Defaults to False.
60+
color (Optional[float], optional): Fix color balance. Defaults to None.
61+
brightness (Optional[float], optional): Fix brightness. Defaults to None.
62+
sharpness (Optional[float], optional): Fix sharpness. Defaults to None.
63+
contrast (Optional[float], optional): Fix contrast. Defaults to None.
64+
tosaka (Optional[float], optional): Convert to grayscale with fix contrast. Defaults to None.
65+
outputrgb (bool, optional): Outputs a monochrome image in RGB. Defaults to False.
66+
cyanotype (bool, optional): Convert to RGB like cyanotype. Defaults to False.
67+
sepia (bool, optional): Convert to RGB colored by sepia. Defaults to False.
68+
noise (Optional[float], optional): Add Gaussian noise. Defaults to None.
69+
line_drawing (bool, optional): Convert to like line drawing. Defaults to False.
70+
version (bool, optional): Show version. Defaults to False.
71+
72+
Raises:
73+
fire.core.FireError: Error that occurs when the specified string is not a path.
74+
75+
Returns:
76+
str: Message.
977
"""
10-
fire.Fire(process)
78+
if version:
79+
return "Anshitsu version {}".format(__version__)
80+
if path is None:
81+
raise fire.core.FireError("No path specified!")
82+
types = ("*.jpg", "*.JPG", "*.jpeg", "*.JPEG", "*.png", "*.PNG")
83+
files_glob = []
84+
return_path = ""
85+
now_s = datetime.datetime.now()
86+
output_dir = "anshitsu_out"
87+
original_dir = "anshitsu_orig"
88+
if os.path.isdir(path):
89+
for type in types:
90+
files_glob.extend(glob.glob(os.path.join(path, '**', type), recursive=True))
91+
files_glob = [file for file in files_glob if not file.__contains__(output_dir)]
92+
return_path = path
1193

94+
if len(files_glob) == 0:
95+
raise fire.core.FireError(
96+
"There are no JPEG or PNG files in this directory."
97+
)
98+
elif os.path.isfile(path):
99+
files_glob.extend(glob.glob(path))
100+
return_path = os.path.abspath(os.path.join(path, os.pardir))
101+
else:
102+
raise fire.core.FireError("A non-path string was passed.")
103+
if overwrite is True:
104+
os.makedirs(os.path.join(return_path, original_dir))
105+
for i, file in enumerate(files_glob):
106+
try:
107+
image = Image.open(file)
108+
except UnidentifiedImageError as e:
109+
raise fire.core.FireError(e)
110+
exif = image.getexif()
111+
original_filename: str = os.path.split(file)[1]
112+
extension = original_filename.split(".")[-1]
113+
timestamp = now_s.strftime("%Y-%m-%d_%H-%M-%S")
114+
if overwrite is True:
115+
backup_filename = original_filename
116+
image.save(os.path.join(
117+
return_path,
118+
original_dir,
119+
backup_filename
120+
))
121+
filename = os.path.join(return_path, re.sub(r"\.[^.]+$", "", original_filename) + ".png")
122+
remove_file_list = [".jpg", ".JPG", ".jpeg", ".JPEG", ".PNG"]
123+
for remove_file in remove_file_list:
124+
remove_file_name = re.sub(r"\.[^.]+$", "", original_filename) + remove_file
125+
remove_file_path = os.path.join(return_path, remove_file_name)
126+
if os.path.isfile(remove_file_path):
127+
os.remove(remove_file_path)
128+
else:
129+
filename = os.path.join(
130+
return_path,
131+
output_dir,
132+
re.sub(r"\.[^.]+$", "_", original_filename)
133+
+ "_{0}_converted_at_{1}.png".format(extension, timestamp))
134+
psr = Processor(
135+
image=image,
136+
colorautoadjust=colorautoadjust,
137+
colorstretch=colorstretch,
138+
grayscale=grayscale,
139+
color=color,
140+
contrast=contrast,
141+
brightness=brightness,
142+
sharpness=sharpness,
143+
invert=invert,
144+
tosaka=tosaka,
145+
outputrgb=outputrgb,
146+
cyanotype=cyanotype,
147+
sepia=sepia,
148+
noise=noise,
149+
line_drawing=line_drawing,
150+
posterize=posterize,
151+
)
152+
saved_image = psr.process()
153+
os.makedirs(os.path.join(return_path, output_dir), exist_ok=True)
154+
saved_image.save(
155+
filename,
156+
quality=100, # Specify 100 as the highest image quality
157+
subsampling=0,
158+
exif=exif,
159+
)
160+
print("{0}/{1} done!".format((i + 1), str(len(files_glob))))
12161

13-
if __name__ == "__main__":
14-
main()
162+
return "The cli was completed successfully."

src/anshitsu/main.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import fire
2+
3+
from anshitsu.cli import cli
4+
5+
6+
def main():
7+
"""
8+
main [summary]
9+
"""
10+
fire.Fire(cli)
11+
12+
13+
if __name__ == "__main__":
14+
main()

0 commit comments

Comments
 (0)