fotolab 0.9.0__py2.py3-none-any.whl → 0.13.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fotolab/__init__.py +31 -1
- fotolab/auto.py +5 -2
- fotolab/border.py +29 -37
- fotolab/cli.py +5 -1
- fotolab/contrast.py +10 -17
- fotolab/env.py +3 -1
- fotolab/info.py +58 -0
- fotolab/montage.py +71 -0
- fotolab/resize.py +12 -20
- fotolab/sharpen.py +13 -22
- fotolab/watermark.py +47 -38
- {fotolab-0.9.0.dist-info → fotolab-0.13.0.dist-info}/METADATA +105 -18
- fotolab-0.13.0.dist-info/RECORD +17 -0
- fotolab-0.9.0.dist-info/RECORD +0 -15
- {fotolab-0.9.0.dist-info → fotolab-0.13.0.dist-info}/LICENSE.md +0 -0
- {fotolab-0.9.0.dist-info → fotolab-0.13.0.dist-info}/WHEEL +0 -0
- {fotolab-0.9.0.dist-info → fotolab-0.13.0.dist-info}/entry_points.txt +0 -0
fotolab/__init__.py
CHANGED
@@ -19,12 +19,42 @@ import logging
|
|
19
19
|
import os
|
20
20
|
import subprocess
|
21
21
|
import sys
|
22
|
+
from pathlib import Path
|
22
23
|
|
23
|
-
__version__ = "0.
|
24
|
+
__version__ = "0.13.0"
|
24
25
|
|
25
26
|
log = logging.getLogger(__name__)
|
26
27
|
|
27
28
|
|
29
|
+
def save_image(args, new_image, output_filename, subcommand):
|
30
|
+
"""Save image after image operation.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
args (argparse.Namespace): Config from command line arguments
|
34
|
+
new_image(PIL.Image.Image): Modified image
|
35
|
+
subcommand(str): Subcommand used to call this function
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
None
|
39
|
+
"""
|
40
|
+
image_file = Path(output_filename)
|
41
|
+
|
42
|
+
if args.overwrite:
|
43
|
+
new_filename = image_file.with_name(image_file.name)
|
44
|
+
else:
|
45
|
+
new_filename = Path(
|
46
|
+
args.output_dir,
|
47
|
+
image_file.with_name(f"{subcommand}_{image_file.name}"),
|
48
|
+
)
|
49
|
+
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
50
|
+
|
51
|
+
log.info("%s image: %s", subcommand, new_filename)
|
52
|
+
new_image.save(new_filename)
|
53
|
+
|
54
|
+
if args.open:
|
55
|
+
_open_image(new_filename)
|
56
|
+
|
57
|
+
|
28
58
|
def _open_image(filename):
|
29
59
|
"""Open generated image using default program."""
|
30
60
|
if sys.platform == "linux":
|
fotolab/auto.py
CHANGED
@@ -35,11 +35,12 @@ def build_subparser(subparsers) -> None:
|
|
35
35
|
auto_parser.set_defaults(func=run)
|
36
36
|
|
37
37
|
auto_parser.add_argument(
|
38
|
-
dest="
|
38
|
+
dest="image_filenames",
|
39
39
|
help="set the image filename",
|
40
|
+
nargs="+",
|
40
41
|
type=str,
|
41
42
|
default=None,
|
42
|
-
metavar="
|
43
|
+
metavar="IMAGE_FILENAMES",
|
43
44
|
)
|
44
45
|
|
45
46
|
|
@@ -65,9 +66,11 @@ def run(args: argparse.Namespace) -> None:
|
|
65
66
|
"font_color": "white",
|
66
67
|
"outline_width": 2,
|
67
68
|
"outline_color": "black",
|
69
|
+
"padding": 15,
|
68
70
|
}
|
69
71
|
combined_args = argparse.Namespace(**vars(args), **extra_args)
|
70
72
|
combined_args.overwrite = True
|
73
|
+
combined_args.open = False
|
71
74
|
log.debug(args)
|
72
75
|
log.debug(combined_args)
|
73
76
|
|
fotolab/border.py
CHANGED
@@ -17,10 +17,11 @@
|
|
17
17
|
|
18
18
|
import argparse
|
19
19
|
import logging
|
20
|
-
from pathlib import Path
|
21
20
|
|
22
21
|
from PIL import Image, ImageColor, ImageOps
|
23
22
|
|
23
|
+
from fotolab import save_image
|
24
|
+
|
24
25
|
log = logging.getLogger(__name__)
|
25
26
|
|
26
27
|
|
@@ -31,11 +32,12 @@ def build_subparser(subparsers) -> None:
|
|
31
32
|
border_parser.set_defaults(func=run)
|
32
33
|
|
33
34
|
border_parser.add_argument(
|
34
|
-
dest="
|
35
|
-
help="set the image
|
35
|
+
dest="image_filenames",
|
36
|
+
help="set the image filenames",
|
37
|
+
nargs="+",
|
36
38
|
type=str,
|
37
39
|
default=None,
|
38
|
-
metavar="
|
40
|
+
metavar="IMAGE_FILENAMES",
|
39
41
|
)
|
40
42
|
|
41
43
|
border_parser.add_argument(
|
@@ -110,38 +112,28 @@ def run(args: argparse.Namespace) -> None:
|
|
110
112
|
"""
|
111
113
|
log.debug(args)
|
112
114
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
fill=ImageColor.getrgb(args.color),
|
136
|
-
)
|
137
|
-
|
138
|
-
if args.overwrite:
|
139
|
-
new_filename = image_file.with_name(image_file.name)
|
140
|
-
else:
|
141
|
-
new_filename = Path(
|
142
|
-
args.output_dir, image_file.with_name(f"border_{image_file.name}")
|
115
|
+
for image_filename in args.image_filenames:
|
116
|
+
original_image = Image.open(image_filename)
|
117
|
+
|
118
|
+
if (
|
119
|
+
args.width_left
|
120
|
+
or args.width_top
|
121
|
+
or args.width_right
|
122
|
+
or args.width_bottom
|
123
|
+
):
|
124
|
+
border = (
|
125
|
+
int(args.width_left),
|
126
|
+
int(args.width_top),
|
127
|
+
int(args.width_right),
|
128
|
+
int(args.width_bottom),
|
129
|
+
)
|
130
|
+
else:
|
131
|
+
border = args.width
|
132
|
+
|
133
|
+
bordered_image = ImageOps.expand(
|
134
|
+
original_image,
|
135
|
+
border=border,
|
136
|
+
fill=ImageColor.getrgb(args.color),
|
143
137
|
)
|
144
|
-
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
145
138
|
|
146
|
-
|
147
|
-
bordered_image.save(new_filename)
|
139
|
+
save_image(args, bordered_image, image_filename, "watermark")
|
fotolab/cli.py
CHANGED
@@ -29,6 +29,8 @@ import fotolab.auto
|
|
29
29
|
import fotolab.border
|
30
30
|
import fotolab.contrast
|
31
31
|
import fotolab.env
|
32
|
+
import fotolab.info
|
33
|
+
import fotolab.montage
|
32
34
|
import fotolab.resize
|
33
35
|
import fotolab.sharpen
|
34
36
|
import fotolab.watermark
|
@@ -82,7 +84,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
82
84
|
default=False,
|
83
85
|
action="store_true",
|
84
86
|
dest="open",
|
85
|
-
help="open the image using default program (default: '%(default)s'",
|
87
|
+
help="open the image using default program (default: '%(default)s')",
|
86
88
|
)
|
87
89
|
|
88
90
|
parser.add_argument(
|
@@ -122,7 +124,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
122
124
|
fotolab.auto.build_subparser(subparsers)
|
123
125
|
fotolab.border.build_subparser(subparsers)
|
124
126
|
fotolab.contrast.build_subparser(subparsers)
|
127
|
+
fotolab.info.build_subparser(subparsers)
|
125
128
|
fotolab.resize.build_subparser(subparsers)
|
129
|
+
fotolab.montage.build_subparser(subparsers)
|
126
130
|
fotolab.sharpen.build_subparser(subparsers)
|
127
131
|
fotolab.watermark.build_subparser(subparsers)
|
128
132
|
fotolab.env.build_subparser(subparsers)
|
fotolab/contrast.py
CHANGED
@@ -17,10 +17,11 @@
|
|
17
17
|
|
18
18
|
import argparse
|
19
19
|
import logging
|
20
|
-
from pathlib import Path
|
21
20
|
|
22
21
|
from PIL import Image, ImageOps
|
23
22
|
|
23
|
+
from fotolab import save_image
|
24
|
+
|
24
25
|
log = logging.getLogger(__name__)
|
25
26
|
|
26
27
|
|
@@ -33,11 +34,12 @@ def build_subparser(subparsers) -> None:
|
|
33
34
|
contrast_parser.set_defaults(func=run)
|
34
35
|
|
35
36
|
contrast_parser.add_argument(
|
36
|
-
dest="
|
37
|
+
dest="image_filenames",
|
37
38
|
help="set the image filename",
|
39
|
+
nargs="+",
|
38
40
|
type=str,
|
39
41
|
default=None,
|
40
|
-
metavar="
|
42
|
+
metavar="IMAGE_FILENAMES",
|
41
43
|
)
|
42
44
|
|
43
45
|
contrast_parser.add_argument(
|
@@ -66,19 +68,10 @@ def run(args: argparse.Namespace) -> None:
|
|
66
68
|
"""
|
67
69
|
log.debug(args)
|
68
70
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
if args.overwrite:
|
75
|
-
new_filename = image_file.with_name(image_file.name)
|
76
|
-
else:
|
77
|
-
new_filename = Path(
|
78
|
-
args.output_dir,
|
79
|
-
image_file.with_name(f"contrast_{image_file.name}"),
|
71
|
+
for image_filename in args.image_filenames:
|
72
|
+
original_image = Image.open(image_filename)
|
73
|
+
contrast_image = ImageOps.autocontrast(
|
74
|
+
original_image, cutoff=args.cutoff
|
80
75
|
)
|
81
|
-
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
82
76
|
|
83
|
-
|
84
|
-
contrast_image.save(new_filename)
|
77
|
+
save_image(args, contrast_image, image_filename, "contrast")
|
fotolab/env.py
CHANGED
@@ -45,6 +45,8 @@ def run(args: argparse.Namespace) -> None:
|
|
45
45
|
"""
|
46
46
|
log.debug(args)
|
47
47
|
|
48
|
+
sys_version = sys.version.replace("\n", "")
|
49
|
+
|
48
50
|
print(f"fotolab: {__version__}")
|
49
|
-
print(f"python: {
|
51
|
+
print(f"python: {sys_version}")
|
50
52
|
print(f"platform: {platform.platform()}")
|
fotolab/info.py
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Copyright (C) 2024 Kian-Meng Ang
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify it under
|
4
|
+
# the terms of the GNU Affero General Public License as published by the Free
|
5
|
+
# Software Foundation, either version 3 of the License, or (at your option) any
|
6
|
+
# later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
10
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
11
|
+
# details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
"""Info subcommand."""
|
17
|
+
|
18
|
+
import argparse
|
19
|
+
import logging
|
20
|
+
|
21
|
+
from PIL import ExifTags, Image
|
22
|
+
|
23
|
+
log = logging.getLogger(__name__)
|
24
|
+
|
25
|
+
|
26
|
+
def build_subparser(subparsers) -> None:
|
27
|
+
"""Build the subparser."""
|
28
|
+
info_parser = subparsers.add_parser("info", help="info an image")
|
29
|
+
|
30
|
+
info_parser.set_defaults(func=run)
|
31
|
+
|
32
|
+
info_parser.add_argument(
|
33
|
+
dest="image_filename",
|
34
|
+
help="set the image filename",
|
35
|
+
type=str,
|
36
|
+
default=None,
|
37
|
+
metavar="IMAGE_FILENAME",
|
38
|
+
)
|
39
|
+
|
40
|
+
|
41
|
+
def run(args: argparse.Namespace) -> None:
|
42
|
+
"""Run info subcommand.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
config (argparse.Namespace): Config from command line arguments
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
None
|
49
|
+
"""
|
50
|
+
log.debug(args)
|
51
|
+
|
52
|
+
image = Image.open(args.image_filename)
|
53
|
+
exif = image._getexif()
|
54
|
+
info = {ExifTags.TAGS.get(tag_id): exif.get(tag_id) for tag_id in exif}
|
55
|
+
|
56
|
+
tag_name_width = max(map(len, info))
|
57
|
+
for tag_name, tag_value in info.items():
|
58
|
+
print(f"{tag_name:<{tag_name_width}}: {tag_value}")
|
fotolab/montage.py
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Copyright (C) 2024 Kian-Meng Ang
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify it under
|
4
|
+
# the terms of the GNU Affero General Public License as published by the Free
|
5
|
+
# Software Foundation, either version 3 of the License, or (at your option) any
|
6
|
+
# later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
10
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
11
|
+
# details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
"""Montage subcommand."""
|
17
|
+
|
18
|
+
import argparse
|
19
|
+
import logging
|
20
|
+
|
21
|
+
from PIL import Image
|
22
|
+
|
23
|
+
from fotolab import save_image
|
24
|
+
|
25
|
+
log = logging.getLogger(__name__)
|
26
|
+
|
27
|
+
|
28
|
+
def build_subparser(subparsers) -> None:
|
29
|
+
"""Build the subparser."""
|
30
|
+
montage_parser = subparsers.add_parser(
|
31
|
+
"montage", help="montage a list of image"
|
32
|
+
)
|
33
|
+
|
34
|
+
montage_parser.set_defaults(func=run)
|
35
|
+
|
36
|
+
montage_parser.add_argument(
|
37
|
+
dest="image_filenames",
|
38
|
+
help="set the image filenames",
|
39
|
+
nargs="+",
|
40
|
+
type=argparse.FileType("r"),
|
41
|
+
default=None,
|
42
|
+
metavar="IMAGE_FILENAMES",
|
43
|
+
)
|
44
|
+
|
45
|
+
|
46
|
+
def run(args: argparse.Namespace) -> None:
|
47
|
+
"""Run montage subcommand.
|
48
|
+
|
49
|
+
Args:
|
50
|
+
config (argparse.Namespace): Config from command line arguments
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
None
|
54
|
+
"""
|
55
|
+
log.debug(args)
|
56
|
+
images = []
|
57
|
+
for image_filename in args.image_filenames:
|
58
|
+
images.append(Image.open(image_filename.name))
|
59
|
+
|
60
|
+
total_width = sum(img.width for img in images)
|
61
|
+
total_height = max(img.height for img in images)
|
62
|
+
|
63
|
+
montaged_image = Image.new("RGB", (total_width, total_height))
|
64
|
+
|
65
|
+
x_offset = 0
|
66
|
+
for img in images:
|
67
|
+
montaged_image.paste(img, (x_offset, 0))
|
68
|
+
x_offset += img.width
|
69
|
+
|
70
|
+
output_image_filename = args.image_filenames[0].name
|
71
|
+
save_image(args, montaged_image, output_image_filename, "montage")
|
fotolab/resize.py
CHANGED
@@ -17,10 +17,11 @@
|
|
17
17
|
|
18
18
|
import argparse
|
19
19
|
import logging
|
20
|
-
from pathlib import Path
|
21
20
|
|
22
21
|
from PIL import Image
|
23
22
|
|
23
|
+
from fotolab import save_image
|
24
|
+
|
24
25
|
log = logging.getLogger(__name__)
|
25
26
|
|
26
27
|
|
@@ -31,11 +32,12 @@ def build_subparser(subparsers) -> None:
|
|
31
32
|
resize_parser.set_defaults(func=run)
|
32
33
|
|
33
34
|
resize_parser.add_argument(
|
34
|
-
dest="
|
35
|
+
dest="image_filenames",
|
35
36
|
help="set the image filename",
|
37
|
+
nargs="+",
|
36
38
|
type=str,
|
37
39
|
default=None,
|
38
|
-
metavar="
|
40
|
+
metavar="IMAGE_FILENAMES",
|
39
41
|
)
|
40
42
|
|
41
43
|
resize_parser.add_argument(
|
@@ -63,28 +65,18 @@ def run(args: argparse.Namespace) -> None:
|
|
63
65
|
"""Run resize subcommand.
|
64
66
|
|
65
67
|
Args:
|
66
|
-
|
68
|
+
args (argparse.Namespace): Config from command line arguments
|
67
69
|
|
68
70
|
Returns:
|
69
71
|
None
|
70
72
|
"""
|
71
73
|
log.debug(args)
|
72
74
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
)
|
79
|
-
|
80
|
-
if args.overwrite:
|
81
|
-
new_filename = image_file.with_name(image_file.name)
|
82
|
-
else:
|
83
|
-
new_filename = Path(
|
84
|
-
args.output_dir,
|
85
|
-
image_file.with_name(f"resize_{image_file.name}"),
|
75
|
+
for image_filename in args.image_filenames:
|
76
|
+
original_image = Image.open(image_filename)
|
77
|
+
resized_image = original_image.copy()
|
78
|
+
resized_image = resized_image.resize(
|
79
|
+
(args.width, args.height), Image.Resampling.LANCZOS
|
86
80
|
)
|
87
|
-
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
88
81
|
|
89
|
-
|
90
|
-
resized_image.save(new_filename)
|
82
|
+
save_image(args, resized_image, image_filename, "resize")
|
fotolab/sharpen.py
CHANGED
@@ -17,10 +17,11 @@
|
|
17
17
|
|
18
18
|
import argparse
|
19
19
|
import logging
|
20
|
-
from pathlib import Path
|
21
20
|
|
22
21
|
from PIL import Image, ImageFilter
|
23
22
|
|
23
|
+
from fotolab import save_image
|
24
|
+
|
24
25
|
log = logging.getLogger(__name__)
|
25
26
|
|
26
27
|
|
@@ -31,11 +32,12 @@ def build_subparser(subparsers) -> None:
|
|
31
32
|
sharpen_parser.set_defaults(func=run)
|
32
33
|
|
33
34
|
sharpen_parser.add_argument(
|
34
|
-
dest="
|
35
|
-
help="set the image
|
35
|
+
dest="image_filenames",
|
36
|
+
help="set the image filenames",
|
37
|
+
nargs="+",
|
36
38
|
type=str,
|
37
39
|
default=None,
|
38
|
-
metavar="
|
40
|
+
metavar="IMAGE_FILENAMES",
|
39
41
|
)
|
40
42
|
|
41
43
|
sharpen_parser.add_argument(
|
@@ -86,22 +88,11 @@ def run(args: argparse.Namespace) -> None:
|
|
86
88
|
"""
|
87
89
|
log.debug(args)
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
)
|
96
|
-
)
|
97
|
-
|
98
|
-
if args.overwrite:
|
99
|
-
new_filename = image_file.with_name(image_file.name)
|
100
|
-
else:
|
101
|
-
new_filename = Path(
|
102
|
-
args.output_dir, image_file.with_name(f"sharpen_{image_file.name}")
|
91
|
+
for image_filename in args.image_filenames:
|
92
|
+
original_image = Image.open(image_filename)
|
93
|
+
sharpen_image = original_image.filter(
|
94
|
+
ImageFilter.UnsharpMask(
|
95
|
+
args.radius, percent=args.percent, threshold=args.threshold
|
96
|
+
)
|
103
97
|
)
|
104
|
-
|
105
|
-
|
106
|
-
log.info("sharpening image: %s", new_filename)
|
107
|
-
sharpen_image.save(new_filename)
|
98
|
+
save_image(args, sharpen_image, image_filename, "sharpen")
|
fotolab/watermark.py
CHANGED
@@ -17,10 +17,11 @@
|
|
17
17
|
|
18
18
|
import argparse
|
19
19
|
import logging
|
20
|
-
from pathlib import Path
|
21
20
|
|
22
21
|
from PIL import Image, ImageColor, ImageDraw, ImageFont
|
23
22
|
|
23
|
+
from fotolab import save_image
|
24
|
+
|
24
25
|
log = logging.getLogger(__name__)
|
25
26
|
|
26
27
|
POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right"]
|
@@ -35,11 +36,12 @@ def build_subparser(subparsers) -> None:
|
|
35
36
|
watermark_parser.set_defaults(func=run)
|
36
37
|
|
37
38
|
watermark_parser.add_argument(
|
38
|
-
dest="
|
39
|
-
help="set the image
|
39
|
+
dest="image_filenames",
|
40
|
+
help="set the image filenames",
|
41
|
+
nargs="+",
|
40
42
|
type=str,
|
41
43
|
default=None,
|
42
|
-
metavar="
|
44
|
+
metavar="IMAGE_FILENAMES",
|
43
45
|
)
|
44
46
|
|
45
47
|
watermark_parser.add_argument(
|
@@ -61,6 +63,19 @@ def build_subparser(subparsers) -> None:
|
|
61
63
|
default="bottom-left",
|
62
64
|
)
|
63
65
|
|
66
|
+
watermark_parser.add_argument(
|
67
|
+
"-pd",
|
68
|
+
"--padding",
|
69
|
+
dest="padding",
|
70
|
+
type=int,
|
71
|
+
default=15,
|
72
|
+
help=(
|
73
|
+
"set the padding of the watermark text relative to the image "
|
74
|
+
"(default: '%(default)s')"
|
75
|
+
),
|
76
|
+
metavar="PADDING",
|
77
|
+
)
|
78
|
+
|
64
79
|
watermark_parser.add_argument(
|
65
80
|
"-fs",
|
66
81
|
"--font-size",
|
@@ -119,49 +134,43 @@ def run(args: argparse.Namespace) -> None:
|
|
119
134
|
"""
|
120
135
|
log.debug(args)
|
121
136
|
|
122
|
-
|
137
|
+
for image_filename in args.image_filenames:
|
138
|
+
original_image = Image.open(image_filename)
|
139
|
+
watermarked_image = original_image.copy()
|
123
140
|
|
124
|
-
|
125
|
-
watermarked_image = original_image.copy()
|
141
|
+
draw = ImageDraw.Draw(watermarked_image)
|
126
142
|
|
127
|
-
|
143
|
+
font = ImageFont.truetype("arial.ttf", args.font_size)
|
128
144
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
(position_x, position_y),
|
142
|
-
args.text,
|
143
|
-
font=font,
|
144
|
-
fill=ImageColor.getrgb(args.font_color),
|
145
|
-
stroke_width=args.outline_width,
|
146
|
-
stroke_fill=ImageColor.getrgb(args.outline_color),
|
147
|
-
)
|
145
|
+
(left, top, right, bottom) = draw.textbbox(
|
146
|
+
xy=(0, 0), text=args.text, font=font
|
147
|
+
)
|
148
|
+
text_width = right - left
|
149
|
+
text_height = bottom - top
|
150
|
+
(position_x, position_y) = calculate_position(
|
151
|
+
watermarked_image,
|
152
|
+
text_width,
|
153
|
+
text_height,
|
154
|
+
args.position,
|
155
|
+
args.padding,
|
156
|
+
)
|
148
157
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
args.
|
154
|
-
|
158
|
+
draw.text(
|
159
|
+
(position_x, position_y),
|
160
|
+
args.text,
|
161
|
+
font=font,
|
162
|
+
fill=(*ImageColor.getrgb(args.font_color), 128),
|
163
|
+
stroke_width=args.outline_width,
|
164
|
+
stroke_fill=(*ImageColor.getrgb(args.outline_color), 128),
|
155
165
|
)
|
156
|
-
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
157
166
|
|
158
|
-
|
159
|
-
watermarked_image.save(new_filename)
|
167
|
+
save_image(args, watermarked_image, image_filename, "watermark")
|
160
168
|
|
161
169
|
|
162
|
-
def calculate_position(
|
170
|
+
def calculate_position(
|
171
|
+
image, text_width, text_height, position, padding
|
172
|
+
) -> tuple:
|
163
173
|
"""Calculate the boundary coordinates of the watermark text."""
|
164
|
-
padding = 10
|
165
174
|
if position == "top-left":
|
166
175
|
position_x = 0 + padding
|
167
176
|
position_y = 0 + padding
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fotolab
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.13.0
|
4
4
|
Summary: A console program that manipulate images.
|
5
5
|
Keywords: photography,photo
|
6
6
|
Author-email: Kian-Meng Ang <kianmeng@cpan.org>
|
@@ -57,9 +57,9 @@ fotolab -h
|
|
57
57
|
```
|
58
58
|
|
59
59
|
```console
|
60
|
-
|
61
60
|
usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-d] [-V]
|
62
|
-
{auto,border,contrast,resize,sharpen,watermark,env}
|
61
|
+
{auto,border,contrast,info,resize,montage,sharpen,watermark,env}
|
62
|
+
...
|
63
63
|
|
64
64
|
A console program to manipulate photos.
|
65
65
|
|
@@ -68,12 +68,14 @@ A console program to manipulate photos.
|
|
68
68
|
issues: https://github.com/kianmeng/fotolab/issues
|
69
69
|
|
70
70
|
positional arguments:
|
71
|
-
{auto,border,contrast,resize,sharpen,watermark,env}
|
71
|
+
{auto,border,contrast,info,resize,montage,sharpen,watermark,env}
|
72
72
|
sub-command help
|
73
73
|
auto auto adjust (resize, contrast, and watermark) a photo
|
74
74
|
border add border to image
|
75
75
|
contrast contrast an image
|
76
|
+
info info an image
|
76
77
|
resize resize an image
|
78
|
+
montage montage a list of image
|
77
79
|
sharpen sharpen an image
|
78
80
|
watermark watermark an image
|
79
81
|
env print environment information for bug reporting
|
@@ -81,7 +83,7 @@ positional arguments:
|
|
81
83
|
optional arguments:
|
82
84
|
-h, --help show this help message and exit
|
83
85
|
-o, --overwrite overwrite existing image
|
84
|
-
-op, --open open the image using default program (default: 'False'
|
86
|
+
-op, --open open the image using default program (default: 'False')
|
85
87
|
-od OUTPUT_DIR, --output-dir OUTPUT_DIR
|
86
88
|
set default output folder (default: 'output')
|
87
89
|
-q, --quiet suppress all logging
|
@@ -89,6 +91,22 @@ optional arguments:
|
|
89
91
|
-V, --version show program's version number and exit
|
90
92
|
```
|
91
93
|
|
94
|
+
### fotolab auto
|
95
|
+
|
96
|
+
```console
|
97
|
+
fotolab auto -h
|
98
|
+
```
|
99
|
+
|
100
|
+
```console
|
101
|
+
usage: fotolab auto [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
102
|
+
|
103
|
+
positional arguments:
|
104
|
+
IMAGE_FILENAMES set the image filename
|
105
|
+
|
106
|
+
optional arguments:
|
107
|
+
-h, --help show this help message and exit
|
108
|
+
```
|
109
|
+
|
92
110
|
### fotolab border
|
93
111
|
|
94
112
|
```console
|
@@ -98,12 +116,12 @@ fotolab border -h
|
|
98
116
|
```console
|
99
117
|
usage: fotolab border [-h] [-c COLOR] [-w WIDTH] [-wt WIDTH] [-wr WIDTH]
|
100
118
|
[-wb WIDTH] [-wl WIDTH]
|
101
|
-
|
119
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
102
120
|
|
103
121
|
positional arguments:
|
104
|
-
|
122
|
+
IMAGE_FILENAMES set the image filenames
|
105
123
|
|
106
|
-
|
124
|
+
optional arguments:
|
107
125
|
-h, --help show this help message and exit
|
108
126
|
-c COLOR, --color COLOR
|
109
127
|
set the color of border (default: 'black')
|
@@ -126,7 +144,26 @@ fotolab contrast -h
|
|
126
144
|
```
|
127
145
|
|
128
146
|
```console
|
129
|
-
usage: fotolab contrast [-h]
|
147
|
+
usage: fotolab contrast [-h] [-c CUTOFF] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
148
|
+
|
149
|
+
positional arguments:
|
150
|
+
IMAGE_FILENAMES set the image filename
|
151
|
+
|
152
|
+
optional arguments:
|
153
|
+
-h, --help show this help message and exit
|
154
|
+
-c CUTOFF, --cutoff CUTOFF
|
155
|
+
set the percentage of lightest or darkest pixels to
|
156
|
+
discard from histogram (default: '1')
|
157
|
+
```
|
158
|
+
|
159
|
+
### fotolab info
|
160
|
+
|
161
|
+
```console
|
162
|
+
fotolab info -h
|
163
|
+
```
|
164
|
+
|
165
|
+
```console
|
166
|
+
usage: fotolab info [-h] IMAGE_FILENAME
|
130
167
|
|
131
168
|
positional arguments:
|
132
169
|
IMAGE_FILENAME set the image filename
|
@@ -135,6 +172,44 @@ optional arguments:
|
|
135
172
|
-h, --help show this help message and exit
|
136
173
|
```
|
137
174
|
|
175
|
+
### fotolab montage
|
176
|
+
|
177
|
+
```console
|
178
|
+
fotolab montage -h
|
179
|
+
```
|
180
|
+
|
181
|
+
```console
|
182
|
+
|
183
|
+
usage: fotolab montage [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
184
|
+
|
185
|
+
positional arguments:
|
186
|
+
IMAGE_FILENAMES set the image filenames
|
187
|
+
|
188
|
+
optional arguments:
|
189
|
+
-h, --help show this help message and exit
|
190
|
+
```
|
191
|
+
|
192
|
+
### fotolab resize
|
193
|
+
|
194
|
+
```console
|
195
|
+
fotolab resize -h
|
196
|
+
```
|
197
|
+
|
198
|
+
```console
|
199
|
+
usage: fotolab resize [-h] [-wh WIDTH] [-ht HEIGHT]
|
200
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
201
|
+
|
202
|
+
positional arguments:
|
203
|
+
IMAGE_FILENAMES set the image filename
|
204
|
+
|
205
|
+
optional arguments:
|
206
|
+
-h, --help show this help message and exit
|
207
|
+
-wh WIDTH, --width WIDTH
|
208
|
+
set the width of the image (default: '600')
|
209
|
+
-ht HEIGHT, --height HEIGHT
|
210
|
+
set the height of the image (default: '277')
|
211
|
+
```
|
212
|
+
|
138
213
|
### fotolab sharpen
|
139
214
|
|
140
215
|
```console
|
@@ -142,13 +217,22 @@ fotolab sharpen -h
|
|
142
217
|
```
|
143
218
|
|
144
219
|
```console
|
145
|
-
usage: fotolab sharpen [-h]
|
220
|
+
usage: fotolab sharpen [-h] [-r RADIUS] [-p PERCENT] [-t THRESHOLD]
|
221
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
146
222
|
|
147
223
|
positional arguments:
|
148
|
-
|
224
|
+
IMAGE_FILENAMES set the image filenames
|
149
225
|
|
150
|
-
|
151
|
-
-h, --help
|
226
|
+
optional arguments:
|
227
|
+
-h, --help show this help message and exit
|
228
|
+
-r RADIUS, --radius RADIUS
|
229
|
+
set the radius or size of edges (default: '1')
|
230
|
+
-p PERCENT, --percent PERCENT
|
231
|
+
set the amount of overall strength of sharpening
|
232
|
+
effect (default: '100')
|
233
|
+
-t THRESHOLD, --threshold THRESHOLD
|
234
|
+
set the minimum brightness changed to be sharpened
|
235
|
+
(default: '3')
|
152
236
|
```
|
153
237
|
|
154
238
|
### fotolab watermark
|
@@ -160,20 +244,23 @@ fotolab watermark -h
|
|
160
244
|
```console
|
161
245
|
usage: fotolab watermark [-h] [-t WATERMARK_TEXT]
|
162
246
|
[-p {top-left,top-right,bottom-left,bottom-right}]
|
163
|
-
[-
|
164
|
-
[-oc OUTLINE_COLOR]
|
165
|
-
|
247
|
+
[-pd PADDING] [-fs FONT_SIZE] [-fc FONT_COLOR]
|
248
|
+
[-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR]
|
249
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
166
250
|
|
167
251
|
positional arguments:
|
168
|
-
|
252
|
+
IMAGE_FILENAMES set the image filenames
|
169
253
|
|
170
|
-
|
254
|
+
optional arguments:
|
171
255
|
-h, --help show this help message and exit
|
172
256
|
-t WATERMARK_TEXT, --text WATERMARK_TEXT
|
173
257
|
set the watermark text (default: 'kianmeng.org')
|
174
258
|
-p {top-left,top-right,bottom-left,bottom-right}, --position {top-left,top-right,bottom-left,bottom-right}
|
175
259
|
set position of the watermark text (default: 'bottom-
|
176
260
|
left')
|
261
|
+
-pd PADDING, --padding PADDING
|
262
|
+
set the padding of the watermark text relative to the
|
263
|
+
image (default: '15')
|
177
264
|
-fs FONT_SIZE, --font-size FONT_SIZE
|
178
265
|
set the font size of watermark text (default: '12')
|
179
266
|
-fc FONT_COLOR, --font-color FONT_COLOR
|
@@ -0,0 +1,17 @@
|
|
1
|
+
fotolab/__init__.py,sha256=Z3guoQD823MHOmrtHNE9W0RnI6neYdRWNCv7QTxQb6s,2074
|
2
|
+
fotolab/__main__.py,sha256=aboOURPs_snOXTEWYR0q8oq1UTY9e-NxCd1j33V0wHI,833
|
3
|
+
fotolab/auto.py,sha256=1Toxe8pA_tq15g1-imMFuHf1L94Ac7EthPTu7E8SAzE,2217
|
4
|
+
fotolab/border.py,sha256=5ch2d7LVPhB2OFuuXSW5ci6Cn967CPDQu0qSfaO7uMg,3591
|
5
|
+
fotolab/cli.py,sha256=KoSDb752mewrtKE5wDs9icXF2McBVOKoEB98KYAV-jQ,4570
|
6
|
+
fotolab/contrast.py,sha256=l7Bs5p8W8ypN9Cg3fFHnU-A20UwMKtjTiPk6D0PRwpM,2095
|
7
|
+
fotolab/env.py,sha256=NTTvfISWBBfIw5opWrUfg0BtkaAtdUtcISBAJC2gVUk,1449
|
8
|
+
fotolab/info.py,sha256=DawXTQJiQDBwy0Ml5Ysk8MvKga3ikp_aIw73AR3LdZo,1687
|
9
|
+
fotolab/montage.py,sha256=lUVY-zDSH7mwH-s34_XefdNp7CoDJHkwpbTUGiyJGgs,2037
|
10
|
+
fotolab/resize.py,sha256=y3JT2IZUOeWf4gjORtaJ_ZseklO2jG6XvR-d6EdhS0k,2242
|
11
|
+
fotolab/sharpen.py,sha256=DsbIe4VU0Ty5oVzKnU70p9dMfhKr1i_UAQDimzAGX-Y,2655
|
12
|
+
fotolab/watermark.py,sha256=6LeK5g6W7Gq5kpLinwqBCwFGCtp0Uz_hrqBE3BD_CEU,5210
|
13
|
+
fotolab-0.13.0.dist-info/entry_points.txt,sha256=mvw7AY_yZkIyjAxPtHNed9X99NZeLnMxEeAfEJUbrCM,44
|
14
|
+
fotolab-0.13.0.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
|
15
|
+
fotolab-0.13.0.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
|
16
|
+
fotolab-0.13.0.dist-info/METADATA,sha256=v1RC94R2HI1BVbz7dyA3nAVuGso-OsRmDZQvrHnCfuU,9085
|
17
|
+
fotolab-0.13.0.dist-info/RECORD,,
|
fotolab-0.9.0.dist-info/RECORD
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
fotolab/__init__.py,sha256=a0NCJBRDthqiPJOuVD1dvsVwDXvm-ruruL26QiEL6q8,1237
|
2
|
-
fotolab/__main__.py,sha256=aboOURPs_snOXTEWYR0q8oq1UTY9e-NxCd1j33V0wHI,833
|
3
|
-
fotolab/auto.py,sha256=q_d_lOZh410nvh9yGNS3k-jg5mB0zQrhOfxSmi-nShA,2142
|
4
|
-
fotolab/border.py,sha256=QBA-rJ0phv_3ace20NW18PlhLIbYDjdGVJwX13vDKdI,3776
|
5
|
-
fotolab/cli.py,sha256=4IEk0DRrCPIG0PB37f4zSyWUEbrZrLSKi5qJxb1M0Is,4433
|
6
|
-
fotolab/contrast.py,sha256=dtQ39YBDmBhFlRS0jB8XqLsoNGEz96ZYfgg71MyeLFg,2350
|
7
|
-
fotolab/env.py,sha256=XUwy3TAFFcTMvtXAT0tLJbo-ianBo_FPK2IQfvBOhic,1400
|
8
|
-
fotolab/resize.py,sha256=ax4IacQR4vuGGxGI3B-ito6ldiT054QkaKympiv4y20,2505
|
9
|
-
fotolab/sharpen.py,sha256=PP1xYXupOetQxQxXijzWiUlrKNlu80lfInG37VQhVYw,2902
|
10
|
-
fotolab/watermark.py,sha256=X7u-HlOONfOhU8VRyvOgIvXYUu_q9IKHBMEkIf-oem4,5031
|
11
|
-
fotolab-0.9.0.dist-info/entry_points.txt,sha256=mvw7AY_yZkIyjAxPtHNed9X99NZeLnMxEeAfEJUbrCM,44
|
12
|
-
fotolab-0.9.0.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
|
13
|
-
fotolab-0.9.0.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
|
14
|
-
fotolab-0.9.0.dist-info/METADATA,sha256=i_R7skWGswlLGC1_GbZWdXWkXTup2iGeQ17iaHRZ8vc,6688
|
15
|
-
fotolab-0.9.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|