fotolab 0.9.1__py2.py3-none-any.whl → 0.14.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 +7 -7
- fotolab/animate.py +114 -0
- fotolab/auto.py +4 -2
- fotolab/border.py +27 -25
- fotolab/cli.py +7 -1
- fotolab/contrast.py +9 -5
- fotolab/info.py +58 -0
- fotolab/montage.py +71 -0
- fotolab/resize.py +10 -8
- fotolab/sharpen.py +11 -9
- fotolab/watermark.py +53 -33
- {fotolab-0.9.1.dist-info → fotolab-0.14.0.dist-info}/METADATA +129 -17
- fotolab-0.14.0.dist-info/RECORD +18 -0
- fotolab-0.9.1.dist-info/RECORD +0 -15
- {fotolab-0.9.1.dist-info → fotolab-0.14.0.dist-info}/LICENSE.md +0 -0
- {fotolab-0.9.1.dist-info → fotolab-0.14.0.dist-info}/WHEEL +0 -0
- {fotolab-0.9.1.dist-info → fotolab-0.14.0.dist-info}/entry_points.txt +0 -0
fotolab/__init__.py
CHANGED
@@ -21,34 +21,34 @@ import subprocess
|
|
21
21
|
import sys
|
22
22
|
from pathlib import Path
|
23
23
|
|
24
|
-
__version__ = "0.
|
24
|
+
__version__ = "0.14.0"
|
25
25
|
|
26
26
|
log = logging.getLogger(__name__)
|
27
27
|
|
28
28
|
|
29
|
-
def save_image(args, new_image):
|
30
|
-
"""
|
29
|
+
def save_image(args, new_image, output_filename, subcommand):
|
30
|
+
"""Save image after image operation.
|
31
31
|
|
32
32
|
Args:
|
33
33
|
args (argparse.Namespace): Config from command line arguments
|
34
34
|
new_image(PIL.Image.Image): Modified image
|
35
|
+
subcommand(str): Subcommand used to call this function
|
35
36
|
|
36
37
|
Returns:
|
37
38
|
None
|
38
39
|
"""
|
39
|
-
|
40
|
-
image_file = Path(args.image_filename)
|
40
|
+
image_file = Path(output_filename)
|
41
41
|
|
42
42
|
if args.overwrite:
|
43
43
|
new_filename = image_file.with_name(image_file.name)
|
44
44
|
else:
|
45
45
|
new_filename = Path(
|
46
46
|
args.output_dir,
|
47
|
-
image_file.with_name(f"{
|
47
|
+
image_file.with_name(f"{subcommand}_{image_file.name}"),
|
48
48
|
)
|
49
49
|
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
50
50
|
|
51
|
-
log.info("%s image: %s",
|
51
|
+
log.info("%s image: %s", subcommand, new_filename)
|
52
52
|
new_image.save(new_filename)
|
53
53
|
|
54
54
|
if args.open:
|
fotolab/animate.py
ADDED
@@ -0,0 +1,114 @@
|
|
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
|
+
"""Animate subcommand."""
|
17
|
+
|
18
|
+
import argparse
|
19
|
+
import logging
|
20
|
+
from pathlib import Path
|
21
|
+
|
22
|
+
from PIL import Image
|
23
|
+
|
24
|
+
from fotolab import _open_image
|
25
|
+
|
26
|
+
log = logging.getLogger(__name__)
|
27
|
+
|
28
|
+
|
29
|
+
def build_subparser(subparsers) -> None:
|
30
|
+
"""Build the subparser."""
|
31
|
+
animate_parser = subparsers.add_parser("animate", help="animate an image")
|
32
|
+
|
33
|
+
animate_parser.set_defaults(func=run)
|
34
|
+
|
35
|
+
animate_parser.add_argument(
|
36
|
+
dest="image_filenames",
|
37
|
+
help="set the image filenames",
|
38
|
+
nargs="+",
|
39
|
+
type=str,
|
40
|
+
default=None,
|
41
|
+
metavar="IMAGE_FILENAMES",
|
42
|
+
)
|
43
|
+
|
44
|
+
animate_parser.add_argument(
|
45
|
+
"-f",
|
46
|
+
"--format",
|
47
|
+
dest="format",
|
48
|
+
type=str,
|
49
|
+
choices=["gif", "webp"],
|
50
|
+
default="gif",
|
51
|
+
help="set the image format (default: '%(default)s')",
|
52
|
+
metavar="FORMAT",
|
53
|
+
)
|
54
|
+
|
55
|
+
animate_parser.add_argument(
|
56
|
+
"-d",
|
57
|
+
"--duration",
|
58
|
+
dest="duration",
|
59
|
+
type=int,
|
60
|
+
default=2500,
|
61
|
+
help="set the duration in milliseconds (default: '%(default)s')",
|
62
|
+
metavar="DURATION",
|
63
|
+
)
|
64
|
+
|
65
|
+
animate_parser.add_argument(
|
66
|
+
"-l",
|
67
|
+
"--loop",
|
68
|
+
dest="loop",
|
69
|
+
type=int,
|
70
|
+
default=0,
|
71
|
+
help="set the loop cycle (default: '%(default)s')",
|
72
|
+
metavar="LOOP",
|
73
|
+
)
|
74
|
+
|
75
|
+
|
76
|
+
def run(args: argparse.Namespace) -> None:
|
77
|
+
"""Run animate subcommand.
|
78
|
+
|
79
|
+
Args:
|
80
|
+
config (argparse.Namespace): Config from command line arguments
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
None
|
84
|
+
"""
|
85
|
+
log.debug(args)
|
86
|
+
|
87
|
+
first_image = args.image_filenames[0]
|
88
|
+
animated_image = Image.open(first_image)
|
89
|
+
|
90
|
+
append_images = []
|
91
|
+
for image_filename in args.image_filenames[1:]:
|
92
|
+
append_images.append(Image.open(image_filename))
|
93
|
+
|
94
|
+
image_file = Path(first_image)
|
95
|
+
new_filename = Path(
|
96
|
+
args.output_dir,
|
97
|
+
image_file.with_name(f"animate_{image_file.stem}.{args.format}"),
|
98
|
+
)
|
99
|
+
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
100
|
+
|
101
|
+
log.info("animate image: %s", new_filename)
|
102
|
+
|
103
|
+
animated_image.save(
|
104
|
+
new_filename,
|
105
|
+
format=args.format,
|
106
|
+
append_images=append_images,
|
107
|
+
save_all=True,
|
108
|
+
duration=args.duration,
|
109
|
+
loop=args.loop,
|
110
|
+
optimize=True,
|
111
|
+
)
|
112
|
+
|
113
|
+
if args.open:
|
114
|
+
_open_image(new_filename)
|
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,6 +66,7 @@ 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
|
fotolab/border.py
CHANGED
@@ -32,11 +32,12 @@ def build_subparser(subparsers) -> None:
|
|
32
32
|
border_parser.set_defaults(func=run)
|
33
33
|
|
34
34
|
border_parser.add_argument(
|
35
|
-
dest="
|
36
|
-
help="set the image
|
35
|
+
dest="image_filenames",
|
36
|
+
help="set the image filenames",
|
37
|
+
nargs="+",
|
37
38
|
type=str,
|
38
39
|
default=None,
|
39
|
-
metavar="
|
40
|
+
metavar="IMAGE_FILENAMES",
|
40
41
|
)
|
41
42
|
|
42
43
|
border_parser.add_argument(
|
@@ -111,27 +112,28 @@ def run(args: argparse.Namespace) -> None:
|
|
111
112
|
"""
|
112
113
|
log.debug(args)
|
113
114
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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),
|
127
137
|
)
|
128
|
-
else:
|
129
|
-
border = args.width
|
130
138
|
|
131
|
-
|
132
|
-
original_image,
|
133
|
-
border=border,
|
134
|
-
fill=ImageColor.getrgb(args.color),
|
135
|
-
)
|
136
|
-
|
137
|
-
save_image(args, bordered_image)
|
139
|
+
save_image(args, bordered_image, image_filename, "watermark")
|
fotolab/cli.py
CHANGED
@@ -25,10 +25,13 @@ import logging
|
|
25
25
|
import sys
|
26
26
|
from typing import Dict, Optional, Sequence
|
27
27
|
|
28
|
+
import fotolab.animate
|
28
29
|
import fotolab.auto
|
29
30
|
import fotolab.border
|
30
31
|
import fotolab.contrast
|
31
32
|
import fotolab.env
|
33
|
+
import fotolab.info
|
34
|
+
import fotolab.montage
|
32
35
|
import fotolab.resize
|
33
36
|
import fotolab.sharpen
|
34
37
|
import fotolab.watermark
|
@@ -82,7 +85,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
82
85
|
default=False,
|
83
86
|
action="store_true",
|
84
87
|
dest="open",
|
85
|
-
help="open the image using default program (default: '%(default)s'",
|
88
|
+
help="open the image using default program (default: '%(default)s')",
|
86
89
|
)
|
87
90
|
|
88
91
|
parser.add_argument(
|
@@ -119,10 +122,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
119
122
|
)
|
120
123
|
|
121
124
|
subparsers = parser.add_subparsers(help="sub-command help")
|
125
|
+
fotolab.animate.build_subparser(subparsers)
|
122
126
|
fotolab.auto.build_subparser(subparsers)
|
123
127
|
fotolab.border.build_subparser(subparsers)
|
124
128
|
fotolab.contrast.build_subparser(subparsers)
|
129
|
+
fotolab.info.build_subparser(subparsers)
|
125
130
|
fotolab.resize.build_subparser(subparsers)
|
131
|
+
fotolab.montage.build_subparser(subparsers)
|
126
132
|
fotolab.sharpen.build_subparser(subparsers)
|
127
133
|
fotolab.watermark.build_subparser(subparsers)
|
128
134
|
fotolab.env.build_subparser(subparsers)
|
fotolab/contrast.py
CHANGED
@@ -34,11 +34,12 @@ def build_subparser(subparsers) -> None:
|
|
34
34
|
contrast_parser.set_defaults(func=run)
|
35
35
|
|
36
36
|
contrast_parser.add_argument(
|
37
|
-
dest="
|
37
|
+
dest="image_filenames",
|
38
38
|
help="set the image filename",
|
39
|
+
nargs="+",
|
39
40
|
type=str,
|
40
41
|
default=None,
|
41
|
-
metavar="
|
42
|
+
metavar="IMAGE_FILENAMES",
|
42
43
|
)
|
43
44
|
|
44
45
|
contrast_parser.add_argument(
|
@@ -67,7 +68,10 @@ def run(args: argparse.Namespace) -> None:
|
|
67
68
|
"""
|
68
69
|
log.debug(args)
|
69
70
|
|
70
|
-
|
71
|
-
|
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
|
75
|
+
)
|
72
76
|
|
73
|
-
|
77
|
+
save_image(args, contrast_image, image_filename, "contrast")
|
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
@@ -32,11 +32,12 @@ def build_subparser(subparsers) -> None:
|
|
32
32
|
resize_parser.set_defaults(func=run)
|
33
33
|
|
34
34
|
resize_parser.add_argument(
|
35
|
-
dest="
|
35
|
+
dest="image_filenames",
|
36
36
|
help="set the image filename",
|
37
|
+
nargs="+",
|
37
38
|
type=str,
|
38
39
|
default=None,
|
39
|
-
metavar="
|
40
|
+
metavar="IMAGE_FILENAMES",
|
40
41
|
)
|
41
42
|
|
42
43
|
resize_parser.add_argument(
|
@@ -71,10 +72,11 @@ def run(args: argparse.Namespace) -> None:
|
|
71
72
|
"""
|
72
73
|
log.debug(args)
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
80
|
+
)
|
79
81
|
|
80
|
-
|
82
|
+
save_image(args, resized_image, image_filename, "resize")
|
fotolab/sharpen.py
CHANGED
@@ -32,11 +32,12 @@ def build_subparser(subparsers) -> None:
|
|
32
32
|
sharpen_parser.set_defaults(func=run)
|
33
33
|
|
34
34
|
sharpen_parser.add_argument(
|
35
|
-
dest="
|
36
|
-
help="set the image
|
35
|
+
dest="image_filenames",
|
36
|
+
help="set the image filenames",
|
37
|
+
nargs="+",
|
37
38
|
type=str,
|
38
39
|
default=None,
|
39
|
-
metavar="
|
40
|
+
metavar="IMAGE_FILENAMES",
|
40
41
|
)
|
41
42
|
|
42
43
|
sharpen_parser.add_argument(
|
@@ -87,10 +88,11 @@ def run(args: argparse.Namespace) -> None:
|
|
87
88
|
"""
|
88
89
|
log.debug(args)
|
89
90
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
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
|
+
)
|
94
97
|
)
|
95
|
-
|
96
|
-
save_image(args, sharpen_image)
|
98
|
+
save_image(args, sharpen_image, image_filename, "sharpen")
|
fotolab/watermark.py
CHANGED
@@ -36,11 +36,12 @@ def build_subparser(subparsers) -> None:
|
|
36
36
|
watermark_parser.set_defaults(func=run)
|
37
37
|
|
38
38
|
watermark_parser.add_argument(
|
39
|
-
dest="
|
40
|
-
help="set the image
|
39
|
+
dest="image_filenames",
|
40
|
+
help="set the image filenames",
|
41
|
+
nargs="+",
|
41
42
|
type=str,
|
42
43
|
default=None,
|
43
|
-
metavar="
|
44
|
+
metavar="IMAGE_FILENAMES",
|
44
45
|
)
|
45
46
|
|
46
47
|
watermark_parser.add_argument(
|
@@ -62,6 +63,19 @@ def build_subparser(subparsers) -> None:
|
|
62
63
|
default="bottom-left",
|
63
64
|
)
|
64
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
|
+
|
65
79
|
watermark_parser.add_argument(
|
66
80
|
"-fs",
|
67
81
|
"--font-size",
|
@@ -120,37 +134,43 @@ def run(args: argparse.Namespace) -> None:
|
|
120
134
|
"""
|
121
135
|
log.debug(args)
|
122
136
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
137
|
+
for image_filename in args.image_filenames:
|
138
|
+
original_image = Image.open(image_filename)
|
139
|
+
watermarked_image = original_image.copy()
|
140
|
+
|
141
|
+
draw = ImageDraw.Draw(watermarked_image)
|
142
|
+
|
143
|
+
font = ImageFont.truetype("arial.ttf", args.font_size)
|
144
|
+
|
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
|
+
)
|
157
|
+
|
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),
|
165
|
+
)
|
166
|
+
|
167
|
+
save_image(args, watermarked_image, image_filename, "watermark")
|
168
|
+
|
169
|
+
|
170
|
+
def calculate_position(
|
171
|
+
image, text_width, text_height, position, padding
|
172
|
+
) -> tuple:
|
152
173
|
"""Calculate the boundary coordinates of the watermark text."""
|
153
|
-
padding = 10
|
154
174
|
if position == "top-left":
|
155
175
|
position_x = 0 + padding
|
156
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.14.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>
|
@@ -59,7 +59,8 @@ fotolab -h
|
|
59
59
|
```console
|
60
60
|
|
61
61
|
usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-d] [-V]
|
62
|
-
{auto,border,contrast,resize,sharpen,watermark,env}
|
62
|
+
{animate,auto,border,contrast,info,resize,montage,sharpen,watermark,env}
|
63
|
+
...
|
63
64
|
|
64
65
|
A console program to manipulate photos.
|
65
66
|
|
@@ -68,12 +69,15 @@ A console program to manipulate photos.
|
|
68
69
|
issues: https://github.com/kianmeng/fotolab/issues
|
69
70
|
|
70
71
|
positional arguments:
|
71
|
-
{auto,border,contrast,resize,sharpen,watermark,env}
|
72
|
+
{animate,auto,border,contrast,info,resize,montage,sharpen,watermark,env}
|
72
73
|
sub-command help
|
74
|
+
animate animate an image
|
73
75
|
auto auto adjust (resize, contrast, and watermark) a photo
|
74
76
|
border add border to image
|
75
77
|
contrast contrast an image
|
78
|
+
info info an image
|
76
79
|
resize resize an image
|
80
|
+
montage montage a list of image
|
77
81
|
sharpen sharpen an image
|
78
82
|
watermark watermark an image
|
79
83
|
env print environment information for bug reporting
|
@@ -81,7 +85,7 @@ positional arguments:
|
|
81
85
|
optional arguments:
|
82
86
|
-h, --help show this help message and exit
|
83
87
|
-o, --overwrite overwrite existing image
|
84
|
-
-op, --open open the image using default program (default: 'False'
|
88
|
+
-op, --open open the image using default program (default: 'False')
|
85
89
|
-od OUTPUT_DIR, --output-dir OUTPUT_DIR
|
86
90
|
set default output folder (default: 'output')
|
87
91
|
-q, --quiet suppress all logging
|
@@ -89,6 +93,45 @@ optional arguments:
|
|
89
93
|
-V, --version show program's version number and exit
|
90
94
|
```
|
91
95
|
|
96
|
+
### fotolab animate
|
97
|
+
|
98
|
+
```console
|
99
|
+
fotolab animate -h
|
100
|
+
```
|
101
|
+
|
102
|
+
```console
|
103
|
+
|
104
|
+
usage: fotolab animate [-h] [-f FORMAT] [-d DURATION] [-l LOOP]
|
105
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
106
|
+
|
107
|
+
positional arguments:
|
108
|
+
IMAGE_FILENAMES set the image filenames
|
109
|
+
|
110
|
+
optional arguments:
|
111
|
+
-h, --help show this help message and exit
|
112
|
+
-f FORMAT, --format FORMAT
|
113
|
+
set the image format (default: 'gif')
|
114
|
+
-d DURATION, --duration DURATION
|
115
|
+
set the duration in milliseconds (default: '2500')
|
116
|
+
-l LOOP, --loop LOOP set the loop cycle (default: '0')
|
117
|
+
```
|
118
|
+
|
119
|
+
### fotolab auto
|
120
|
+
|
121
|
+
```console
|
122
|
+
fotolab auto -h
|
123
|
+
```
|
124
|
+
|
125
|
+
```console
|
126
|
+
usage: fotolab auto [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
127
|
+
|
128
|
+
positional arguments:
|
129
|
+
IMAGE_FILENAMES set the image filename
|
130
|
+
|
131
|
+
optional arguments:
|
132
|
+
-h, --help show this help message and exit
|
133
|
+
```
|
134
|
+
|
92
135
|
### fotolab border
|
93
136
|
|
94
137
|
```console
|
@@ -98,12 +141,12 @@ fotolab border -h
|
|
98
141
|
```console
|
99
142
|
usage: fotolab border [-h] [-c COLOR] [-w WIDTH] [-wt WIDTH] [-wr WIDTH]
|
100
143
|
[-wb WIDTH] [-wl WIDTH]
|
101
|
-
|
144
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
102
145
|
|
103
146
|
positional arguments:
|
104
|
-
|
147
|
+
IMAGE_FILENAMES set the image filenames
|
105
148
|
|
106
|
-
|
149
|
+
optional arguments:
|
107
150
|
-h, --help show this help message and exit
|
108
151
|
-c COLOR, --color COLOR
|
109
152
|
set the color of border (default: 'black')
|
@@ -126,7 +169,26 @@ fotolab contrast -h
|
|
126
169
|
```
|
127
170
|
|
128
171
|
```console
|
129
|
-
usage: fotolab contrast [-h]
|
172
|
+
usage: fotolab contrast [-h] [-c CUTOFF] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
173
|
+
|
174
|
+
positional arguments:
|
175
|
+
IMAGE_FILENAMES set the image filename
|
176
|
+
|
177
|
+
optional arguments:
|
178
|
+
-h, --help show this help message and exit
|
179
|
+
-c CUTOFF, --cutoff CUTOFF
|
180
|
+
set the percentage of lightest or darkest pixels to
|
181
|
+
discard from histogram (default: '1')
|
182
|
+
```
|
183
|
+
|
184
|
+
### fotolab info
|
185
|
+
|
186
|
+
```console
|
187
|
+
fotolab info -h
|
188
|
+
```
|
189
|
+
|
190
|
+
```console
|
191
|
+
usage: fotolab info [-h] IMAGE_FILENAME
|
130
192
|
|
131
193
|
positional arguments:
|
132
194
|
IMAGE_FILENAME set the image filename
|
@@ -135,6 +197,44 @@ optional arguments:
|
|
135
197
|
-h, --help show this help message and exit
|
136
198
|
```
|
137
199
|
|
200
|
+
### fotolab montage
|
201
|
+
|
202
|
+
```console
|
203
|
+
fotolab montage -h
|
204
|
+
```
|
205
|
+
|
206
|
+
```console
|
207
|
+
|
208
|
+
usage: fotolab montage [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
209
|
+
|
210
|
+
positional arguments:
|
211
|
+
IMAGE_FILENAMES set the image filenames
|
212
|
+
|
213
|
+
optional arguments:
|
214
|
+
-h, --help show this help message and exit
|
215
|
+
```
|
216
|
+
|
217
|
+
### fotolab resize
|
218
|
+
|
219
|
+
```console
|
220
|
+
fotolab resize -h
|
221
|
+
```
|
222
|
+
|
223
|
+
```console
|
224
|
+
usage: fotolab resize [-h] [-wh WIDTH] [-ht HEIGHT]
|
225
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
226
|
+
|
227
|
+
positional arguments:
|
228
|
+
IMAGE_FILENAMES set the image filename
|
229
|
+
|
230
|
+
optional arguments:
|
231
|
+
-h, --help show this help message and exit
|
232
|
+
-wh WIDTH, --width WIDTH
|
233
|
+
set the width of the image (default: '600')
|
234
|
+
-ht HEIGHT, --height HEIGHT
|
235
|
+
set the height of the image (default: '277')
|
236
|
+
```
|
237
|
+
|
138
238
|
### fotolab sharpen
|
139
239
|
|
140
240
|
```console
|
@@ -142,13 +242,22 @@ fotolab sharpen -h
|
|
142
242
|
```
|
143
243
|
|
144
244
|
```console
|
145
|
-
usage: fotolab sharpen [-h]
|
245
|
+
usage: fotolab sharpen [-h] [-r RADIUS] [-p PERCENT] [-t THRESHOLD]
|
246
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
146
247
|
|
147
248
|
positional arguments:
|
148
|
-
|
249
|
+
IMAGE_FILENAMES set the image filenames
|
149
250
|
|
150
|
-
|
151
|
-
-h, --help
|
251
|
+
optional arguments:
|
252
|
+
-h, --help show this help message and exit
|
253
|
+
-r RADIUS, --radius RADIUS
|
254
|
+
set the radius or size of edges (default: '1')
|
255
|
+
-p PERCENT, --percent PERCENT
|
256
|
+
set the amount of overall strength of sharpening
|
257
|
+
effect (default: '100')
|
258
|
+
-t THRESHOLD, --threshold THRESHOLD
|
259
|
+
set the minimum brightness changed to be sharpened
|
260
|
+
(default: '3')
|
152
261
|
```
|
153
262
|
|
154
263
|
### fotolab watermark
|
@@ -160,20 +269,23 @@ fotolab watermark -h
|
|
160
269
|
```console
|
161
270
|
usage: fotolab watermark [-h] [-t WATERMARK_TEXT]
|
162
271
|
[-p {top-left,top-right,bottom-left,bottom-right}]
|
163
|
-
[-
|
164
|
-
[-oc OUTLINE_COLOR]
|
165
|
-
|
272
|
+
[-pd PADDING] [-fs FONT_SIZE] [-fc FONT_COLOR]
|
273
|
+
[-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR]
|
274
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
166
275
|
|
167
276
|
positional arguments:
|
168
|
-
|
277
|
+
IMAGE_FILENAMES set the image filenames
|
169
278
|
|
170
|
-
|
279
|
+
optional arguments:
|
171
280
|
-h, --help show this help message and exit
|
172
281
|
-t WATERMARK_TEXT, --text WATERMARK_TEXT
|
173
282
|
set the watermark text (default: 'kianmeng.org')
|
174
283
|
-p {top-left,top-right,bottom-left,bottom-right}, --position {top-left,top-right,bottom-left,bottom-right}
|
175
284
|
set position of the watermark text (default: 'bottom-
|
176
285
|
left')
|
286
|
+
-pd PADDING, --padding PADDING
|
287
|
+
set the padding of the watermark text relative to the
|
288
|
+
image (default: '15')
|
177
289
|
-fs FONT_SIZE, --font-size FONT_SIZE
|
178
290
|
set the font size of watermark text (default: '12')
|
179
291
|
-fc FONT_COLOR, --font-color FONT_COLOR
|
@@ -0,0 +1,18 @@
|
|
1
|
+
fotolab/__init__.py,sha256=cpDAMSgpSA1exqkB5BGL2OrPVQfTzxV8nOTS5iP0rUE,2074
|
2
|
+
fotolab/__main__.py,sha256=aboOURPs_snOXTEWYR0q8oq1UTY9e-NxCd1j33V0wHI,833
|
3
|
+
fotolab/animate.py,sha256=ejimhTozo9DN7BbqqcV4x8zLnanZRKq1pxBBFeOdr6Q,2967
|
4
|
+
fotolab/auto.py,sha256=1Toxe8pA_tq15g1-imMFuHf1L94Ac7EthPTu7E8SAzE,2217
|
5
|
+
fotolab/border.py,sha256=5ch2d7LVPhB2OFuuXSW5ci6Cn967CPDQu0qSfaO7uMg,3591
|
6
|
+
fotolab/cli.py,sha256=ZbXkftGAIrlcOgWju6KIeCBA8-EDVop2mkRN5f1Sa34,4641
|
7
|
+
fotolab/contrast.py,sha256=l7Bs5p8W8ypN9Cg3fFHnU-A20UwMKtjTiPk6D0PRwpM,2095
|
8
|
+
fotolab/env.py,sha256=NTTvfISWBBfIw5opWrUfg0BtkaAtdUtcISBAJC2gVUk,1449
|
9
|
+
fotolab/info.py,sha256=DawXTQJiQDBwy0Ml5Ysk8MvKga3ikp_aIw73AR3LdZo,1687
|
10
|
+
fotolab/montage.py,sha256=lUVY-zDSH7mwH-s34_XefdNp7CoDJHkwpbTUGiyJGgs,2037
|
11
|
+
fotolab/resize.py,sha256=y3JT2IZUOeWf4gjORtaJ_ZseklO2jG6XvR-d6EdhS0k,2242
|
12
|
+
fotolab/sharpen.py,sha256=DsbIe4VU0Ty5oVzKnU70p9dMfhKr1i_UAQDimzAGX-Y,2655
|
13
|
+
fotolab/watermark.py,sha256=6LeK5g6W7Gq5kpLinwqBCwFGCtp0Uz_hrqBE3BD_CEU,5210
|
14
|
+
fotolab-0.14.0.dist-info/entry_points.txt,sha256=mvw7AY_yZkIyjAxPtHNed9X99NZeLnMxEeAfEJUbrCM,44
|
15
|
+
fotolab-0.14.0.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
|
16
|
+
fotolab-0.14.0.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
|
17
|
+
fotolab-0.14.0.dist-info/METADATA,sha256=jHoSNuIcWosz5li1NRXRTHRxpIFX3sjPv11jFAi7kMQ,9744
|
18
|
+
fotolab-0.14.0.dist-info/RECORD,,
|
fotolab-0.9.1.dist-info/RECORD
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
fotolab/__init__.py,sha256=3N1D57pcYWYmpOUqPIomNqEWg4qVTyVQP6AUvKRNGOg,2015
|
2
|
-
fotolab/__main__.py,sha256=aboOURPs_snOXTEWYR0q8oq1UTY9e-NxCd1j33V0wHI,833
|
3
|
-
fotolab/auto.py,sha256=k4DkHQp05m7UYUgHRrH9L1d0eSgOzrn2Cyk5BG27KyA,2173
|
4
|
-
fotolab/border.py,sha256=-XNa118OpiXwCXptSCl1HdxTJ2SSnv62HSg6xfBChaY,3413
|
5
|
-
fotolab/cli.py,sha256=4IEk0DRrCPIG0PB37f4zSyWUEbrZrLSKi5qJxb1M0Is,4433
|
6
|
-
fotolab/contrast.py,sha256=chIjzw2h7TA_u63Y_ZNS_0otxtyXgHmHaCDOMUayS_A,1969
|
7
|
-
fotolab/env.py,sha256=NTTvfISWBBfIw5opWrUfg0BtkaAtdUtcISBAJC2gVUk,1449
|
8
|
-
fotolab/resize.py,sha256=6314jLmhwb_ktd6uKEToaNHW0ZOyPW2IiELosBsIcXs,2128
|
9
|
-
fotolab/sharpen.py,sha256=5lLWZ2JOM-jfsqr6Lt9tlijZH1oz4KlvBytTL0-QLQI,2535
|
10
|
-
fotolab/watermark.py,sha256=Hn3nUhQo3iFhvtlsLjGjFMGG8rqKFNGOYQBcwRS250M,4648
|
11
|
-
fotolab-0.9.1.dist-info/entry_points.txt,sha256=mvw7AY_yZkIyjAxPtHNed9X99NZeLnMxEeAfEJUbrCM,44
|
12
|
-
fotolab-0.9.1.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
|
13
|
-
fotolab-0.9.1.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
|
14
|
-
fotolab-0.9.1.dist-info/METADATA,sha256=ukyF71SeL7LHXiYzH90aXgo6xzA94Q3aQ_iZjZDRr7s,6688
|
15
|
-
fotolab-0.9.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|