Skip to content

Commit 16cea14

Browse files
committed
v2.1.0
1 parent 98c200e commit 16cea14

File tree

11 files changed

+229
-70
lines changed

11 files changed

+229
-70
lines changed

poetry.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[virtualenvs]
2+
in-project = true

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "anshitsu"
3-
version = "2.0.2" # using poetry-dynamic-versioning
3+
version = "2.1.0" # using poetry-dynamic-versioning
44
description = "A tiny digital photographic utility."
55
readme = "README.md"
66
authors = ["Iosif Takakura <[email protected]>"]

src/anshitsu/cli.py

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,25 @@
1414

1515

1616
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,
17+
path: Optional[str] = None,
18+
keep_alpha: bool = False,
19+
colorautoadjust: bool = False,
20+
colorstretch: bool = False,
21+
grayscale: bool = False,
22+
invert: bool = False,
23+
color: Optional[float] = None,
24+
brightness: Optional[float] = None,
25+
sharpness: Optional[float] = None,
26+
contrast: Optional[float] = None,
27+
tosaka: Optional[float] = None,
28+
outputrgb: bool = False,
29+
sepia: bool = False,
30+
cyanotype: bool = False,
31+
noise: Optional[float] = None,
32+
overwrite: bool = False,
33+
version: bool = False,
34+
line_drawing: bool = False,
35+
posterize: Optional[int] = None,
3536
) -> str:
3637
"""
3738
Process Runnner for Command Line Interface
@@ -87,7 +88,7 @@ def cli(
8788
original_dir = "anshitsu_orig"
8889
if os.path.isdir(path):
8990
for type in types:
90-
files_glob.extend(glob.glob(os.path.join(path, '**', type), recursive=True))
91+
files_glob.extend(glob.glob(os.path.join(path, "**", type), recursive=True))
9192
files_glob = [file for file in files_glob if not file.__contains__(output_dir)]
9293
return_path = path
9394

@@ -113,15 +114,15 @@ def cli(
113114
timestamp = now_s.strftime("%Y-%m-%d_%H-%M-%S")
114115
if overwrite is True:
115116
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")
117+
image.save(os.path.join(return_path, original_dir, backup_filename))
118+
filename = os.path.join(
119+
return_path, re.sub(r"\.[^.]+$", "", original_filename) + ".png"
120+
)
122121
remove_file_list = [".jpg", ".JPG", ".jpeg", ".JPEG", ".PNG"]
123122
for remove_file in remove_file_list:
124-
remove_file_name = re.sub(r"\.[^.]+$", "", original_filename) + remove_file
123+
remove_file_name = (
124+
re.sub(r"\.[^.]+$", "", original_filename) + remove_file
125+
)
125126
remove_file_path = os.path.join(return_path, remove_file_name)
126127
if os.path.isfile(remove_file_path):
127128
os.remove(remove_file_path)
@@ -130,9 +131,11 @@ def cli(
130131
return_path,
131132
output_dir,
132133
re.sub(r"\.[^.]+$", "_", original_filename)
133-
+ "_{0}_converted_at_{1}.png".format(extension, timestamp))
134+
+ "_{0}_converted_at_{1}.png".format(extension, timestamp),
135+
)
134136
psr = Processor(
135137
image=image,
138+
keep_alpha=keep_alpha,
136139
colorautoadjust=colorautoadjust,
137140
colorstretch=colorstretch,
138141
grayscale=grayscale,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from typing import Union
2+
3+
from PIL import Image
4+
5+
6+
def create_alpha_mask(image: Image) -> Union[Image, None]:
7+
"""
8+
create_alpha_mask
9+
10+
If image has alpha layer, create alpha mask.
11+
12+
Parameters:
13+
image: Image
14+
15+
Returns:
16+
Image: cutout alpha mask.
17+
None: Image has not alpha mask.
18+
"""
19+
if image.mode == "RGBA":
20+
_, _, _, alpha = image.split()
21+
alpha_channel = 255
22+
alpha.paste(Image.new("L", image.size, alpha_channel), mask=alpha)
23+
return alpha
24+
elif image.mode == "LA":
25+
_, alpha = image.split()
26+
alpha_channel = 255
27+
alpha.paste(Image.new("L", image.size, alpha_channel), mask=alpha)
28+
return alpha
29+
else:
30+
return None

src/anshitsu/process/line_drawing.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33

44
def line_drawing(image: Image, invert: bool) -> Image:
5+
"""
6+
line_drawing
7+
8+
Paint like line drawing on an image.
9+
If invert is True, the image will be inverted.
10+
11+
Parameters:
12+
image: Image
13+
invert: bool
14+
15+
Returns:
16+
Image: processed image.
17+
"""
518
image = image.convert("L")
619
img_filter = image.filter(ImageFilter.MaxFilter(5))
720
line_draw = ImageChops.difference(image, img_filter)

src/anshitsu/process/noise.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ def noise(image: Image, noise: float) -> Image:
1818
"""
1919
table = [x * 2 for x in range(256)] * len(image.getbands())
2020
if image.mode == "RGB":
21-
noise_image = Image.effect_noise(
22-
(image.width, image.height), noise
23-
).convert("RGB")
21+
noise_image = Image.effect_noise((image.width, image.height), noise).convert(
22+
"RGB"
23+
)
2424
image = ImageChops.multiply(image, noise_image).point(table)
2525
if image.mode == "L":
26-
noise_image = Image.effect_noise(
27-
(image.width, image.height), noise
28-
)
26+
noise_image = Image.effect_noise((image.width, image.height), noise)
2927
image = ImageChops.multiply(image, noise_image).point(table)
3028
return image

src/anshitsu/process/processor.py

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from anshitsu.process.remove_alpha import remove_alpha
1818
from anshitsu.process.sepia import sepia
1919
from anshitsu.process.sharpness import sharpness
20+
from anshitsu.process.create_alpha_mask import create_alpha_mask
2021

2122

2223
class Processor:
@@ -36,23 +37,24 @@ class Processor:
3637
)
3738

3839
def __init__(
39-
self,
40-
image: Image,
41-
colorautoadjust: bool = False,
42-
colorstretch: bool = False,
43-
grayscale: bool = False,
44-
line_drawing: bool = False,
45-
invert: bool = False,
46-
tosaka: Optional[float] = None,
47-
outputrgb: bool = False,
48-
noise: Optional[float] = None,
49-
color: Optional[float] = None,
50-
brightness: Optional[float] = None,
51-
sharpness: Optional[float] = None,
52-
contrast: Optional[float] = None,
53-
sepia: bool = False,
54-
cyanotype: bool = False,
55-
posterize: Optional[int] = None,
40+
self,
41+
image: Image,
42+
colorautoadjust: bool = False,
43+
colorstretch: bool = False,
44+
grayscale: bool = False,
45+
line_drawing: bool = False,
46+
invert: bool = False,
47+
tosaka: Optional[float] = None,
48+
outputrgb: bool = False,
49+
noise: Optional[float] = None,
50+
color: Optional[float] = None,
51+
brightness: Optional[float] = None,
52+
sharpness: Optional[float] = None,
53+
contrast: Optional[float] = None,
54+
sepia: bool = False,
55+
cyanotype: bool = False,
56+
posterize: Optional[int] = None,
57+
keep_alpha: bool = False,
5658
) -> None:
5759
"""
5860
__init__ constructor.
@@ -63,11 +65,21 @@ def __init__(
6365
colorstretch (bool, optional): Use colorstretch algorithm. Defaults to False.
6466
grayscale (bool, optional): Convert to grayscale. Defaults to False.
6567
invert (bool, optional): Invert color. Defaults to False.
68+
color (Optional[float], optional): Fix color balance. Defaults to None.
69+
brightness (Optional[float], optional): Fix brightness. Defaults to None.
70+
sharpness (Optional[float], optional): Fix sharpness. Defaults to None.
71+
contrast (Optional[float], optional): Fix contrast. Defaults to None.
6672
tosaka (Optional[float], optional): Use Tosaka mode. Defaults to None.
6773
outputrgb (bool, optional): Outputs a monochrome image in RGB. Defaults to False.
6874
noise (Optional[float], optional): Add Gaussian noise. Defaults to None.
75+
cyanotype (bool, optional): Convert to RGB like cyanotype. Defaults to False.
76+
sepia (bool, optional): Convert to RGB colored by sepia. Defaults to False.
77+
noise (Optional[float], optional): Add Gaussian noise. Defaults to None.
78+
line_drawing (bool, optional): Convert to like line drawing. Defaults to False.
79+
keep_alpha (bool, optional): Keep alpha mask. Defaults to False.
6980
"""
7081
self.image = image
82+
self.keep_alpha = keep_alpha
7183
self.line_drawing = line_drawing
7284
self.colorautoadjust = colorautoadjust
7385
self.colorstretch = colorstretch
@@ -87,6 +99,11 @@ def __init__(
8799
self.posterize = posterize
88100

89101
def process(self) -> Image:
102+
if self.keep_alpha:
103+
alpha = create_alpha_mask(self.image)
104+
else:
105+
alpha = None
106+
90107
self.image = remove_alpha(self.image)
91108

92109
if self.invert:
@@ -131,17 +148,7 @@ def process(self) -> Image:
131148
if self.cyanotype:
132149
self.image = cyanotype(self.image)
133150

134-
return self.image
151+
if alpha is not None:
152+
self.image.putalpha(alpha)
135153

136-
def __output_rgb(self) -> Image:
137-
"""
138-
__output_rgb
139-
140-
Outputs a monochrome image in RGB.
141-
142-
Returns:
143-
Image: processed image.
144-
"""
145-
if self.image.mode == "L":
146-
self.image = self.image.convert("RGB")
147154
return self.image
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from PIL import Image
2+
3+
4+
def put_alpha_mask(image: Image, alpha: Image) -> Image:
5+
"""
6+
put_alpha_mask
7+
8+
Adds or replaces the alpha layer in this image.
9+
10+
Parameters:
11+
image: Image
12+
alpha: Image
13+
14+
Returns:
15+
Image: Image having alpha layer.
16+
"""
17+
return image.putalpha(alpha)

src/anshitsu/process/sepia.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ def sepia(image: Image) -> Image:
1111
Image: processed image.
1212
"""
1313
if image.mode == "L":
14-
image = Image.merge("RGB",
15-
(
16-
image.point(lambda x: x * 247 / 255),
17-
image.point(lambda x: x * 225 / 255),
18-
image.point(lambda x: x * 194 / 255)
19-
)
20-
)
14+
image = Image.merge(
15+
"RGB",
16+
(
17+
image.point(lambda x: x * 247 / 255),
18+
image.point(lambda x: x * 225 / 255),
19+
image.point(lambda x: x * 194 / 255),
20+
),
21+
)
2122
return image

tests/test_alpha.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import os
2+
3+
from PIL import Image
4+
5+
from anshitsu.process.create_alpha_mask import create_alpha_mask
6+
from anshitsu.process.put_alpha_mask import put_alpha_mask
7+
8+
def test_alpha_mask_by_rgba(setup):
9+
image = Image.open(
10+
os.path.join(".", "tests", "pic", "nullpo.png"),
11+
)
12+
alpha = create_alpha_mask(image)
13+
assert alpha.mode == "L"
14+
15+
16+
def test_alpha_mask_by_grayscale_with_alpha(setup):
17+
image = Image.open(
18+
os.path.join(".", "tests", "pic", "test.png"),
19+
)
20+
alpha = create_alpha_mask(image)
21+
assert alpha.mode == "L"
22+
23+
def test_alpha_by_rgb(setup):
24+
image = Image.open(
25+
os.path.join(".", "tests", "pic", "dog.jpg"),
26+
)
27+
alpha = create_alpha_mask(image)
28+
assert alpha is None
29+
30+
def test_alpha_by_grayscale(setup):
31+
image = Image.open(
32+
os.path.join(".", "tests", "pic", "tokyo_station.jpg"),
33+
)
34+
alpha = create_alpha_mask(image)
35+
assert alpha is None
36+
37+
def test_alpha_by_rgba(setup):
38+
image = Image.open(
39+
os.path.join(".", "tests", "pic", "nullpo.png"),
40+
)
41+
alpha = create_alpha_mask(image)
42+
image.putalpha(alpha)
43+
assert image.mode == "RGBA"
44+
45+
46+
def test_alpha_by_grayscale_with_alpha(setup):
47+
image = Image.open(
48+
os.path.join(".", "tests", "pic", "test.png"),
49+
)
50+
alpha = create_alpha_mask(image)
51+
image.putalpha(alpha)
52+
assert image.mode == "LA"

0 commit comments

Comments
 (0)