fotolab 0.28.6__py3-none-any.whl → 0.29.0__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 +1 -1
- fotolab/cli.py +15 -6
- fotolab/subcommands/halftone.py +59 -13
- {fotolab-0.28.6.dist-info → fotolab-0.29.0.dist-info}/METADATA +1 -1
- {fotolab-0.28.6.dist-info → fotolab-0.29.0.dist-info}/RECORD +8 -8
- {fotolab-0.28.6.dist-info → fotolab-0.29.0.dist-info}/LICENSE.md +0 -0
- {fotolab-0.28.6.dist-info → fotolab-0.29.0.dist-info}/WHEEL +0 -0
- {fotolab-0.28.6.dist-info → fotolab-0.29.0.dist-info}/entry_points.txt +0 -0
fotolab/__init__.py
CHANGED
fotolab/cli.py
CHANGED
@@ -115,6 +115,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
115
115
|
subparsers = parser.add_subparsers(
|
116
116
|
help="sub-command help",
|
117
117
|
dest="command",
|
118
|
+
required=True,
|
118
119
|
)
|
119
120
|
fotolab.subcommands.build_subparser(subparsers)
|
120
121
|
|
@@ -128,16 +129,24 @@ def main(args: Optional[Sequence[str]] = None) -> None:
|
|
128
129
|
|
129
130
|
try:
|
130
131
|
parser = build_parser()
|
131
|
-
if len(args) == 0:
|
132
|
-
parser.print_help(sys.stderr)
|
133
|
-
return
|
134
|
-
|
135
132
|
parsed_args = parser.parse_args(args)
|
136
133
|
setup_logging(parsed_args)
|
137
134
|
|
138
|
-
if
|
135
|
+
if parsed_args.command is not None:
|
139
136
|
log.debug(parsed_args)
|
140
|
-
|
137
|
+
# Ensure the function attribute exists (set by set_defaults in
|
138
|
+
# subcommands)
|
139
|
+
if hasattr(parsed_args, "func"):
|
140
|
+
parsed_args.func(parsed_args)
|
141
|
+
else:
|
142
|
+
# This case should ideally not happen if subcommands are set up
|
143
|
+
# correctly
|
144
|
+
log.error(
|
145
|
+
"subcommand '%s' is missing its execution function.",
|
146
|
+
parsed_args.command
|
147
|
+
)
|
148
|
+
parser.print_help(sys.stderr)
|
149
|
+
raise SystemExit(1)
|
141
150
|
else:
|
142
151
|
parser.print_help(sys.stderr)
|
143
152
|
|
fotolab/subcommands/halftone.py
CHANGED
@@ -69,6 +69,24 @@ def build_subparser(subparsers) -> None:
|
|
69
69
|
help="set default output folder (default: '%(default)s')",
|
70
70
|
)
|
71
71
|
|
72
|
+
halftone_parser.add_argument(
|
73
|
+
"-c",
|
74
|
+
"--cells",
|
75
|
+
dest="cells",
|
76
|
+
type=int,
|
77
|
+
default=50,
|
78
|
+
help="set number of cells across the image width (default: %(default)s)",
|
79
|
+
)
|
80
|
+
|
81
|
+
halftone_parser.add_argument(
|
82
|
+
"-g",
|
83
|
+
"--grayscale",
|
84
|
+
default=False,
|
85
|
+
action="store_true",
|
86
|
+
dest="grayscale",
|
87
|
+
help="convert image to grayscale before applying halftone",
|
88
|
+
)
|
89
|
+
|
72
90
|
|
73
91
|
def run(args: argparse.Namespace) -> None:
|
74
92
|
"""Run halftone subcommand.
|
@@ -83,7 +101,9 @@ def run(args: argparse.Namespace) -> None:
|
|
83
101
|
|
84
102
|
for image_filename in args.image_filenames:
|
85
103
|
original_image = Image.open(image_filename)
|
86
|
-
halftone_image = create_halftone_image(
|
104
|
+
halftone_image = create_halftone_image(
|
105
|
+
original_image, args.cells, args.grayscale
|
106
|
+
)
|
87
107
|
|
88
108
|
if args.before_after:
|
89
109
|
save_gif_image(
|
@@ -98,7 +118,7 @@ def run(args: argparse.Namespace) -> None:
|
|
98
118
|
|
99
119
|
|
100
120
|
def create_halftone_image(
|
101
|
-
original_image: Image.Image, cell_count: int =
|
121
|
+
original_image: Image.Image, cell_count: int, grayscale: bool = False
|
102
122
|
) -> Image.Image:
|
103
123
|
"""Create a halftone version of the input image.
|
104
124
|
|
@@ -107,15 +127,26 @@ def create_halftone_image(
|
|
107
127
|
|
108
128
|
Args:
|
109
129
|
original_image: The source image to convert
|
110
|
-
cell_count: Number of cells across the width
|
130
|
+
cell_count: Number of cells across the width
|
131
|
+
grayscale: Whether to convert to grayscale first (default: False)
|
111
132
|
|
112
133
|
Returns:
|
113
134
|
Image.Image: The halftone converted image
|
114
135
|
"""
|
115
|
-
|
136
|
+
if grayscale:
|
137
|
+
source_image = original_image.convert("L")
|
138
|
+
output_mode = "L"
|
139
|
+
fill_color_black = 0
|
140
|
+
fill_color_dot = 255
|
141
|
+
else:
|
142
|
+
source_image = original_image.convert("RGB")
|
143
|
+
output_mode = "RGB"
|
144
|
+
fill_color_black = (0, 0, 0)
|
145
|
+
|
116
146
|
width, height = original_image.size
|
117
147
|
|
118
|
-
|
148
|
+
# Create a new image for the output, initialized to black
|
149
|
+
halftone_image = Image.new(output_mode, (width, height), fill_color_black)
|
119
150
|
draw = ImageDraw.Draw(halftone_image)
|
120
151
|
|
121
152
|
cellsize = width / cell_count
|
@@ -127,19 +158,34 @@ def create_halftone_image(
|
|
127
158
|
x = int(col * cellsize + cellsize / 2)
|
128
159
|
y = int(row * cellsize + cellsize / 2)
|
129
160
|
|
130
|
-
# Get brightness
|
131
|
-
|
132
|
-
|
161
|
+
# Get pixel value (brightness or color) from the source image
|
162
|
+
pixel_value = source_image.getpixel((x, y))
|
163
|
+
|
164
|
+
if grayscale:
|
165
|
+
brightness = pixel_value
|
166
|
+
dot_fill = fill_color_dot # Use white for grayscale dots
|
167
|
+
else:
|
168
|
+
# Calculate brightness (luminance) from the RGB color
|
169
|
+
brightness = int(
|
170
|
+
0.299 * pixel_value[0]
|
171
|
+
+ 0.587 * pixel_value[1]
|
172
|
+
+ 0.114 * pixel_value[2]
|
173
|
+
)
|
174
|
+
dot_fill = pixel_value # Use original color for color dots
|
175
|
+
|
176
|
+
# Calculate dot radius relative to cell size based on brightness
|
177
|
+
# Max radius is half the cell size. Scale by brightness (0-255).
|
178
|
+
dot_radius = (brightness / 255.0) * (cellsize / 2)
|
133
179
|
|
134
180
|
# Draw the dot
|
135
181
|
draw.ellipse(
|
136
182
|
[
|
137
|
-
x -
|
138
|
-
y -
|
139
|
-
x +
|
140
|
-
y +
|
183
|
+
x - dot_radius,
|
184
|
+
y - dot_radius,
|
185
|
+
x + dot_radius,
|
186
|
+
y + dot_radius,
|
141
187
|
],
|
142
|
-
fill=
|
188
|
+
fill=dot_fill,
|
143
189
|
)
|
144
190
|
|
145
191
|
return halftone_image
|
@@ -1,21 +1,21 @@
|
|
1
|
-
fotolab/__init__.py,sha256=
|
1
|
+
fotolab/__init__.py,sha256=E76E5fEYHIL3PDbEllLqBALGLmzPYpgtdk4B1-ujU_g,3262
|
2
2
|
fotolab/__main__.py,sha256=aboOURPs_snOXTEWYR0q8oq1UTY9e-NxCd1j33V0wHI,833
|
3
|
-
fotolab/cli.py,sha256=
|
3
|
+
fotolab/cli.py,sha256=NH_u73SJhzwDJSRqpi1I4dc8wBfcqbDxNne3cio163A,4411
|
4
4
|
fotolab/subcommands/__init__.py,sha256=l3DlIaJ3u3jGjnC1H1yV8LZ_nPqOLJ6gikD4BCaMAQ0,1129
|
5
5
|
fotolab/subcommands/animate.py,sha256=OcS6Q6JM1TW6HF0dSgBsV9mpEGwDMcJlQssupkf0_ZA,3393
|
6
6
|
fotolab/subcommands/auto.py,sha256=ia-xegV1Z4HvYsbKgmTzf1NfNFdTDPWfZe7vQ1_90Ik,2425
|
7
7
|
fotolab/subcommands/border.py,sha256=BS3BHytdWiNumxdKulKYK-WigbsKtPxECdvInUhUjSQ,4608
|
8
8
|
fotolab/subcommands/contrast.py,sha256=ZHTvAJhRYZjNTrkHRZq3O6wba3LS7QjH1BfiGf4ZvGY,3005
|
9
9
|
fotolab/subcommands/env.py,sha256=QoxRvzZKgmoHTUxDV4QYhdChCpMWs5TbXFY_qIpIQpE,1469
|
10
|
-
fotolab/subcommands/halftone.py,sha256=
|
10
|
+
fotolab/subcommands/halftone.py,sha256=dtqmyZyGC8huzSGRCRgp8t11m51hFC15kvgfEJBm_0c,5714
|
11
11
|
fotolab/subcommands/info.py,sha256=Qp-Zu7Xp1ptLK211hOkFM5oxZWGTrlNBqCpbx2IjL9Y,3371
|
12
12
|
fotolab/subcommands/montage.py,sha256=d_3EcyRSFS8fKkczlHO8IRP-mrKhQUtkQndjfd0MKsc,2566
|
13
13
|
fotolab/subcommands/resize.py,sha256=UOb2rg_5ArRj0gxPTDOww_ix3tqJRC0W5b_S-Lt1G4w,5444
|
14
14
|
fotolab/subcommands/rotate.py,sha256=uBFjHyjiBSQLtrtH1p9myODIHUDr1gkL4PpU-6Y1Ofo,2575
|
15
15
|
fotolab/subcommands/sharpen.py,sha256=YNho2IPbc-lPvSy3Bsjehc2JOEy27LPqFSGRULs9MyY,3492
|
16
16
|
fotolab/subcommands/watermark.py,sha256=iEuek0BM2PJlGMLq8VZhHxaQ8BInuN2_mvssA7Dhgy8,10032
|
17
|
-
fotolab-0.
|
18
|
-
fotolab-0.
|
19
|
-
fotolab-0.
|
20
|
-
fotolab-0.
|
21
|
-
fotolab-0.
|
17
|
+
fotolab-0.29.0.dist-info/entry_points.txt,sha256=mvw7AY_yZkIyjAxPtHNed9X99NZeLnMxEeAfEJUbrCM,44
|
18
|
+
fotolab-0.29.0.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
|
19
|
+
fotolab-0.29.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
|
20
|
+
fotolab-0.29.0.dist-info/METADATA,sha256=h3twSy_UW9ri5GZ0_Xyh0s2IYH6AdVCAll-TuKYc-Xg,12970
|
21
|
+
fotolab-0.29.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|