py-img-processor 1.2.0__tar.gz → 1.2.3__tar.gz
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.
Potentially problematic release.
This version of py-img-processor might be problematic. Click here for more details.
- {py_img_processor-1.2.0/py_img_processor.egg-info → py_img_processor-1.2.3}/PKG-INFO +2 -2
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/__init__.py +1 -1
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/__init__.py +11 -11
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/alpha.py +2 -2
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/base.py +58 -36
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/blur.py +2 -2
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/circle.py +2 -2
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/crop.py +10 -10
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/gray.py +1 -1
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/merge.py +36 -23
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/resize.py +19 -15
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/rotate.py +2 -2
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/parsers/watermark.py +41 -23
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/processor.py +21 -22
- {py_img_processor-1.2.0 → py_img_processor-1.2.3/py_img_processor.egg-info}/PKG-INFO +2 -2
- py_img_processor-1.2.3/py_img_processor.egg-info/requires.txt +2 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/setup.py +1 -1
- py_img_processor-1.2.0/py_img_processor.egg-info/requires.txt +0 -2
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/LICENSE +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/MANIFEST.in +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/README.md +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/enums.py +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/exceptions.py +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/main.py +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/imgprocessor/utils.py +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/py_img_processor.egg-info/SOURCES.txt +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/py_img_processor.egg-info/dependency_links.txt +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/py_img_processor.egg-info/entry_points.txt +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/py_img_processor.egg-info/not-zip-safe +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/py_img_processor.egg-info/top_level.txt +0 -0
- {py_img_processor-1.2.0 → py_img_processor-1.2.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: py-img-processor
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: Image editor using Python and Pillow.
|
|
5
5
|
Home-page: https://github.com/SkylerHu/py-img-processor.git
|
|
6
6
|
Author: SkylerHu
|
|
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
21
21
|
Requires-Python: >=3.9
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
|
-
Requires-Dist: py-enum>=
|
|
24
|
+
Requires-Dist: py-enum>=2.1.1
|
|
25
25
|
Requires-Dist: Pillow>=8
|
|
26
26
|
|
|
27
27
|
# py-img-processor
|
|
@@ -17,16 +17,16 @@ from .watermark import WatermarkParser
|
|
|
17
17
|
from .merge import MergeParser
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
_ACTION_PARASER_MAP: dict[str,
|
|
21
|
-
enums.OpAction.RESIZE: ResizeParser,
|
|
22
|
-
enums.OpAction.CROP: CropParser,
|
|
23
|
-
enums.OpAction.CIRCLE: CircleParser,
|
|
24
|
-
enums.OpAction.BLUR: BlurParser,
|
|
25
|
-
enums.OpAction.ROTATE: RotateParser,
|
|
26
|
-
enums.OpAction.ALPHA: AlphaParser,
|
|
27
|
-
enums.OpAction.GRAY: GrayParser,
|
|
28
|
-
enums.OpAction.WATERMARK: WatermarkParser,
|
|
29
|
-
enums.OpAction.MERGE: MergeParser,
|
|
20
|
+
_ACTION_PARASER_MAP: dict[str, typing.Any] = {
|
|
21
|
+
enums.OpAction.RESIZE.value: ResizeParser,
|
|
22
|
+
enums.OpAction.CROP.value: CropParser,
|
|
23
|
+
enums.OpAction.CIRCLE.value: CircleParser,
|
|
24
|
+
enums.OpAction.BLUR.value: BlurParser,
|
|
25
|
+
enums.OpAction.ROTATE.value: RotateParser,
|
|
26
|
+
enums.OpAction.ALPHA.value: AlphaParser,
|
|
27
|
+
enums.OpAction.GRAY.value: GrayParser,
|
|
28
|
+
enums.OpAction.WATERMARK.value: WatermarkParser,
|
|
29
|
+
enums.OpAction.MERGE.value: MergeParser,
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
|
|
@@ -77,7 +77,7 @@ class ProcessParams(object):
|
|
|
77
77
|
key, param_str = info
|
|
78
78
|
if not key:
|
|
79
79
|
raise ParamParseException(f"参数必须指定操作类型 [{item}]不符合参数要求")
|
|
80
|
-
if key in [enums.OpAction.FORMAT, enums.OpAction.QUALITY, enums.OpAction.INTERLACE]:
|
|
80
|
+
if key in [enums.OpAction.FORMAT.value, enums.OpAction.QUALITY.value, enums.OpAction.INTERLACE.value]:
|
|
81
81
|
save_args.append(f"{key}_{param_str}")
|
|
82
82
|
else:
|
|
83
83
|
action_cls = _ACTION_PARASER_MAP.get(key)
|
|
@@ -10,10 +10,10 @@ from .base import BaseParser, pre_processing
|
|
|
10
10
|
|
|
11
11
|
class AlphaParser(BaseParser):
|
|
12
12
|
|
|
13
|
-
KEY = enums.OpAction.ALPHA
|
|
13
|
+
KEY = enums.OpAction.ALPHA.value
|
|
14
14
|
ARGS = {
|
|
15
15
|
# 不透明度, 为100时,完全不透明,即原图; 为0时,完全透明
|
|
16
|
-
"value": {"type": enums.ArgType.INTEGER, "default": 100, "min": 0, "max": 100},
|
|
16
|
+
"value": {"type": enums.ArgType.INTEGER.value, "default": 100, "min": 0, "max": 100},
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
def __init__(
|
|
@@ -7,9 +7,11 @@ import re
|
|
|
7
7
|
import tempfile
|
|
8
8
|
import urllib.parse
|
|
9
9
|
from urllib.request import urlretrieve
|
|
10
|
+
from contextlib import contextmanager
|
|
10
11
|
|
|
11
12
|
from PIL import Image, ImageOps
|
|
12
13
|
|
|
14
|
+
from py_enum import ChoiceEnum
|
|
13
15
|
from imgprocessor import settings, enums, utils
|
|
14
16
|
from imgprocessor.exceptions import ParamValidateException, ParamParseException, ProcessLimitException
|
|
15
17
|
|
|
@@ -67,21 +69,23 @@ class BaseParser(object):
|
|
|
67
69
|
else:
|
|
68
70
|
value = kwargs.get(key)
|
|
69
71
|
try:
|
|
70
|
-
if _type == enums.ArgType.INTEGER:
|
|
72
|
+
if _type == enums.ArgType.INTEGER.value:
|
|
71
73
|
value = cls._validate_number(value, **config)
|
|
72
|
-
elif _type == enums.ArgType.FLOAT:
|
|
74
|
+
elif _type == enums.ArgType.FLOAT.value:
|
|
73
75
|
value = cls._validate_number(value, use_float=True, **config)
|
|
74
|
-
elif _type == enums.ArgType.STRING:
|
|
76
|
+
elif _type == enums.ArgType.STRING.value:
|
|
75
77
|
value = cls._validate_str(value, enable_base64=enable_base64, **config)
|
|
76
|
-
elif _type == enums.ArgType.URI:
|
|
78
|
+
elif _type == enums.ArgType.URI.value:
|
|
77
79
|
value = cls._validate_uri(value, enable_base64=enable_base64, **config)
|
|
78
|
-
elif _type == enums.ArgType.ACTION:
|
|
80
|
+
elif _type == enums.ArgType.ACTION.value:
|
|
79
81
|
if value and isinstance(value, str):
|
|
80
82
|
value = cls._validate_str(value, enable_base64=enable_base64, **config)
|
|
81
83
|
|
|
82
84
|
choices = config.get("choices")
|
|
85
|
+
if isinstance(choices, ChoiceEnum):
|
|
86
|
+
choices = choices.values
|
|
83
87
|
if choices and value not in choices:
|
|
84
|
-
raise ParamValidateException(f"{key}枚举值只能是其中之一 {choices
|
|
88
|
+
raise ParamValidateException(f"{key}枚举值只能是其中之一 {choices}")
|
|
85
89
|
except ParamValidateException as e:
|
|
86
90
|
raise ParamValidateException(f"参数 {key}={value} 不符合要求:{e}")
|
|
87
91
|
data[key] = value
|
|
@@ -241,23 +245,23 @@ def compute_by_geography(
|
|
|
241
245
|
src_w: int, src_h: int, x: int, y: int, w: int, h: int, g: typing.Optional[str], pf: str
|
|
242
246
|
) -> tuple[int, int]:
|
|
243
247
|
"""计算 大小(w,h)的图像相对于(src_w, src_h)图像的原点(x,y)位置"""
|
|
244
|
-
if g == enums.Geography.NW:
|
|
248
|
+
if g == enums.Geography.NW.value:
|
|
245
249
|
x, y = 0, 0
|
|
246
|
-
elif g == enums.Geography.NORTH:
|
|
250
|
+
elif g == enums.Geography.NORTH.value:
|
|
247
251
|
x, y = int(src_w / 2 - w / 2), 0
|
|
248
|
-
elif g == enums.Geography.NE:
|
|
252
|
+
elif g == enums.Geography.NE.value:
|
|
249
253
|
x, y = src_w - w, 0
|
|
250
|
-
elif g == enums.Geography.WEST:
|
|
254
|
+
elif g == enums.Geography.WEST.value:
|
|
251
255
|
x, y = 0, int(src_h / 2 - h / 2)
|
|
252
|
-
elif g == enums.Geography.CENTER:
|
|
256
|
+
elif g == enums.Geography.CENTER.value:
|
|
253
257
|
x, y = int(src_w / 2 - w / 2), int(src_h / 2 - h / 2)
|
|
254
|
-
elif g == enums.Geography.EAST:
|
|
258
|
+
elif g == enums.Geography.EAST.value:
|
|
255
259
|
x, y = src_w - w, int(src_h / 2 - h / 2)
|
|
256
|
-
elif g == enums.Geography.SW:
|
|
260
|
+
elif g == enums.Geography.SW.value:
|
|
257
261
|
x, y = 0, src_h - h
|
|
258
|
-
elif g == enums.Geography.SOUTH:
|
|
262
|
+
elif g == enums.Geography.SOUTH.value:
|
|
259
263
|
x, y = int(src_w / 2 - w / 2), src_h - h
|
|
260
|
-
elif g == enums.Geography.SE:
|
|
264
|
+
elif g == enums.Geography.SE.value:
|
|
261
265
|
x, y = src_w - w, src_h - h
|
|
262
266
|
elif pf:
|
|
263
267
|
if "x" in pf:
|
|
@@ -302,8 +306,8 @@ def compute_splice_two_im(
|
|
|
302
306
|
h1: int,
|
|
303
307
|
w2: int,
|
|
304
308
|
h2: int,
|
|
305
|
-
align: int = enums.PositionAlign.VERTIAL_CENTER,
|
|
306
|
-
order: int = enums.PositionOrder.BEFORE,
|
|
309
|
+
align: int = enums.PositionAlign.VERTIAL_CENTER.value,
|
|
310
|
+
order: int = enums.PositionOrder.BEFORE.value,
|
|
307
311
|
interval: int = 0,
|
|
308
312
|
) -> tuple:
|
|
309
313
|
"""拼接2个图像,计算整体大小和元素原点位置;数值单位都是像素
|
|
@@ -325,33 +329,37 @@ def compute_splice_two_im(
|
|
|
325
329
|
第2个元素的原点位置x2
|
|
326
330
|
第2个元素的原点位置y2
|
|
327
331
|
"""
|
|
328
|
-
if align in [
|
|
332
|
+
if align in [
|
|
333
|
+
enums.PositionAlign.TOP.value,
|
|
334
|
+
enums.PositionAlign.HORIZONTAL_CENTER.value,
|
|
335
|
+
enums.PositionAlign.BOTTOM.value,
|
|
336
|
+
]:
|
|
329
337
|
# 水平顺序
|
|
330
338
|
# 计算整体占位大小w,h
|
|
331
339
|
w, h = w1 + w2 + interval, max(h1, h2)
|
|
332
340
|
|
|
333
|
-
if align == enums.PositionAlign.TOP:
|
|
341
|
+
if align == enums.PositionAlign.TOP.value:
|
|
334
342
|
y1, y2 = 0, 0
|
|
335
|
-
elif align == enums.PositionAlign.BOTTOM:
|
|
343
|
+
elif align == enums.PositionAlign.BOTTOM.value:
|
|
336
344
|
y1, y2 = h - h1, h - h2
|
|
337
345
|
else:
|
|
338
346
|
y1, y2 = int((h - h1) / 2), int((h - h2) / 2)
|
|
339
347
|
|
|
340
|
-
if order == enums.PositionOrder.BEFORE:
|
|
348
|
+
if order == enums.PositionOrder.BEFORE.value:
|
|
341
349
|
x1, x2 = 0, w1 + interval
|
|
342
350
|
else:
|
|
343
351
|
x1, x2 = w2 + interval, 0
|
|
344
352
|
else:
|
|
345
353
|
# 垂直
|
|
346
354
|
w, h = max(w1, w2), h1 + h2 + interval
|
|
347
|
-
if align == enums.PositionAlign.LEFT:
|
|
355
|
+
if align == enums.PositionAlign.LEFT.value:
|
|
348
356
|
x1, x2 = 0, 0
|
|
349
|
-
elif align == enums.PositionAlign.RIGHT:
|
|
357
|
+
elif align == enums.PositionAlign.RIGHT.value:
|
|
350
358
|
x1, x2 = w - w1, w - w2
|
|
351
359
|
else:
|
|
352
360
|
x1, x2 = int((w - w1) / 2), int((w - w2) / 2)
|
|
353
361
|
|
|
354
|
-
if order == enums.PositionOrder.BEFORE:
|
|
362
|
+
if order == enums.PositionOrder.BEFORE.value:
|
|
355
363
|
y1, y2 = 0, h1 + interval
|
|
356
364
|
else:
|
|
357
365
|
y1, y2 = h2 + interval, 0
|
|
@@ -369,11 +377,22 @@ def validate_ori_im(ori_im: Image) -> None:
|
|
|
369
377
|
raise ProcessLimitException(f"图像总像素不可超过{settings.PROCESSOR_MAX_PIXEL}像素,输入图像({src_w}, {src_h})")
|
|
370
378
|
|
|
371
379
|
|
|
372
|
-
def
|
|
380
|
+
def copy_full_img(ori_im: Image) -> Image:
|
|
381
|
+
out_im = ori_im.copy()
|
|
382
|
+
# 复制格式信息
|
|
383
|
+
out_im.format = ori_im.format
|
|
384
|
+
# 复制info中的元数据(包括ICC配置文件等)
|
|
385
|
+
out_im.info = ori_im.info.copy()
|
|
386
|
+
return out_im
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@contextmanager
|
|
390
|
+
def trans_uri_to_im(uri: str, use_copy: bool = False) -> typing.Generator:
|
|
373
391
|
"""将输入资源转换成Image对象
|
|
374
392
|
|
|
375
393
|
Args:
|
|
376
394
|
uri: 文件路径 或者 可下载的链接地址
|
|
395
|
+
use_copy: 是否复制图像,使其不依赖打开的文件
|
|
377
396
|
|
|
378
397
|
Raises:
|
|
379
398
|
ProcessLimitException: 处理图像大小/像素限制
|
|
@@ -392,27 +411,30 @@ def trans_uri_to_im(uri: str) -> Image:
|
|
|
392
411
|
if size > settings.PROCESSOR_MAX_FILE_SIZE * 1024 * 1024:
|
|
393
412
|
raise ProcessLimitException(f"图像文件大小不得超过{settings.PROCESSOR_MAX_FILE_SIZE}MB")
|
|
394
413
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
414
|
+
with Image.open(fp) as uri_im:
|
|
415
|
+
validate_ori_im(uri_im)
|
|
416
|
+
# 解决临时文件close后im对象不能正常使用得问题
|
|
417
|
+
ori_im = copy_full_img(uri_im)
|
|
418
|
+
yield ori_im
|
|
399
419
|
else:
|
|
400
420
|
size = os.path.getsize(uri)
|
|
401
421
|
if size > settings.PROCESSOR_MAX_FILE_SIZE * 1024 * 1024:
|
|
402
422
|
raise ProcessLimitException(f"图像文件大小不得超过{settings.PROCESSOR_MAX_FILE_SIZE}MB")
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
423
|
+
with Image.open(uri) as uri_im:
|
|
424
|
+
validate_ori_im(uri_im)
|
|
425
|
+
ori_im = uri_im
|
|
426
|
+
if use_copy:
|
|
427
|
+
ori_im = copy_full_img(ori_im)
|
|
428
|
+
yield ori_im
|
|
407
429
|
|
|
408
430
|
|
|
409
431
|
class ImgSaveParser(BaseParser):
|
|
410
432
|
KEY = ""
|
|
411
433
|
ARGS = {
|
|
412
|
-
"format": {"type": enums.ArgType.STRING, "default": None},
|
|
413
|
-
"quality": {"type": enums.ArgType.INTEGER, "default": None, "min": 1, "max": 100},
|
|
434
|
+
"format": {"type": enums.ArgType.STRING.value, "default": None},
|
|
435
|
+
"quality": {"type": enums.ArgType.INTEGER.value, "default": None, "min": 1, "max": 100},
|
|
414
436
|
# 1 表示将原图设置成渐进显示
|
|
415
|
-
"interlace": {"type": enums.ArgType.INTEGER, "default": 0, "choices": [0, 1]},
|
|
437
|
+
"interlace": {"type": enums.ArgType.INTEGER.value, "default": 0, "choices": [0, 1]},
|
|
416
438
|
}
|
|
417
439
|
|
|
418
440
|
def __init__(
|
|
@@ -10,10 +10,10 @@ from .base import BaseParser, pre_processing
|
|
|
10
10
|
|
|
11
11
|
class BlurParser(BaseParser):
|
|
12
12
|
|
|
13
|
-
KEY = enums.OpAction.BLUR
|
|
13
|
+
KEY = enums.OpAction.BLUR.value
|
|
14
14
|
ARGS = {
|
|
15
15
|
# 模糊半径,值越大,图片越模糊
|
|
16
|
-
"r": {"type": enums.ArgType.INTEGER, "required": True, "min": 1, "max": 50},
|
|
16
|
+
"r": {"type": enums.ArgType.INTEGER.value, "required": True, "min": 1, "max": 50},
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
def __init__(
|
|
@@ -10,9 +10,9 @@ from .base import BaseParser, pre_processing
|
|
|
10
10
|
|
|
11
11
|
class CircleParser(BaseParser):
|
|
12
12
|
|
|
13
|
-
KEY = enums.OpAction.CIRCLE
|
|
13
|
+
KEY = enums.OpAction.CIRCLE.value
|
|
14
14
|
ARGS = {
|
|
15
|
-
"r": {"type": enums.ArgType.INTEGER, "default": 0, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
15
|
+
"r": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
def __init__(
|
|
@@ -9,20 +9,20 @@ from .base import BaseParser, pre_processing, compute_by_geography, compute_by_r
|
|
|
9
9
|
|
|
10
10
|
class CropParser(BaseParser):
|
|
11
11
|
|
|
12
|
-
KEY = enums.OpAction.CROP
|
|
12
|
+
KEY = enums.OpAction.CROP.value
|
|
13
13
|
ARGS = {
|
|
14
|
-
"w": {"type": enums.ArgType.INTEGER, "default": 0, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
15
|
-
"h": {"type": enums.ArgType.INTEGER, "default": 0, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
16
|
-
"ratio": {"type": enums.ArgType.STRING, "regex": r"^\d+:\d+$"},
|
|
17
|
-
"x": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
18
|
-
"y": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
19
|
-
"g": {"type": enums.ArgType.STRING, "choices": enums.Geography},
|
|
14
|
+
"w": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
15
|
+
"h": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
16
|
+
"ratio": {"type": enums.ArgType.STRING.value, "regex": r"^\d+:\d+$"},
|
|
17
|
+
"x": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
18
|
+
"y": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
19
|
+
"g": {"type": enums.ArgType.STRING.value, "choices": enums.Geography},
|
|
20
20
|
# percent field, eg: xywh
|
|
21
|
-
"pf": {"type": enums.ArgType.STRING, "default": ""},
|
|
21
|
+
"pf": {"type": enums.ArgType.STRING.value, "default": ""},
|
|
22
22
|
# padding right
|
|
23
|
-
"padr": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
23
|
+
"padr": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
24
24
|
# padding bottom
|
|
25
|
-
"padb": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
25
|
+
"padb": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
26
26
|
# 左和上通过x,y控制
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -6,33 +6,43 @@ from PIL import Image
|
|
|
6
6
|
|
|
7
7
|
from imgprocessor import enums, settings
|
|
8
8
|
from imgprocessor.exceptions import ParamValidateException
|
|
9
|
-
from .base import
|
|
9
|
+
from .base import (
|
|
10
|
+
BaseParser,
|
|
11
|
+
pre_processing,
|
|
12
|
+
compute_by_geography,
|
|
13
|
+
compute_splice_two_im,
|
|
14
|
+
trans_uri_to_im,
|
|
15
|
+
)
|
|
10
16
|
|
|
11
17
|
|
|
12
18
|
class MergeParser(BaseParser):
|
|
13
19
|
|
|
14
|
-
KEY = enums.OpAction.MERGE
|
|
20
|
+
KEY = enums.OpAction.MERGE.value
|
|
15
21
|
ARGS = {
|
|
16
22
|
# 要处理的图片
|
|
17
|
-
"image": {"type": enums.ArgType.URI, "required": True, "base64_encode": True},
|
|
23
|
+
"image": {"type": enums.ArgType.URI.value, "required": True, "base64_encode": True},
|
|
18
24
|
# 对image的处理参数
|
|
19
|
-
"actions": {"type": enums.ArgType.ACTION, "base64_encode": True},
|
|
20
|
-
# 使用输入图像的大小作为参照进行缩放
|
|
21
|
-
"p": {"type": enums.ArgType.INTEGER, "default": 0, "min": 1, "max": 1000},
|
|
25
|
+
"actions": {"type": enums.ArgType.ACTION.value, "base64_encode": True},
|
|
22
26
|
# 是否将imgae当做背景放在输入图像之下; 定义输入图像和image参数的拼接顺序
|
|
23
|
-
"bg": {"type": enums.ArgType.INTEGER, "default": 0, "choices": [0, 1]},
|
|
27
|
+
"bg": {"type": enums.ArgType.INTEGER.value, "default": 0, "choices": [0, 1]},
|
|
28
|
+
# 使用输入图像的大小作为参照进行缩放,bg=1按照image缩放输入图像
|
|
29
|
+
"p": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 1, "max": 1000},
|
|
24
30
|
# 对齐方式
|
|
25
|
-
"order": {"type": enums.ArgType.INTEGER, "choices": enums.PositionOrder},
|
|
26
|
-
"align": {
|
|
27
|
-
|
|
31
|
+
"order": {"type": enums.ArgType.INTEGER.value, "choices": enums.PositionOrder},
|
|
32
|
+
"align": {
|
|
33
|
+
"type": enums.ArgType.INTEGER.value,
|
|
34
|
+
"default": enums.PositionAlign.BOTTOM.value,
|
|
35
|
+
"choices": enums.PositionAlign,
|
|
36
|
+
},
|
|
37
|
+
"interval": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": 1000},
|
|
28
38
|
# 粘贴的位置
|
|
29
|
-
"g": {"type": enums.ArgType.STRING, "choices": enums.Geography},
|
|
30
|
-
"x": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
31
|
-
"y": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
32
|
-
"pf": {"type": enums.ArgType.STRING, "default": ""},
|
|
39
|
+
"g": {"type": enums.ArgType.STRING.value, "choices": enums.Geography},
|
|
40
|
+
"x": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
41
|
+
"y": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
42
|
+
"pf": {"type": enums.ArgType.STRING.value, "default": ""},
|
|
33
43
|
# 拼接后大小包含2个图像,空白区域使用color颜色填充
|
|
34
44
|
"color": {
|
|
35
|
-
"type": enums.ArgType.STRING,
|
|
45
|
+
"type": enums.ArgType.STRING.value,
|
|
36
46
|
"default": "0000", # 为了保证透明背景
|
|
37
47
|
"regex": r"^([0-9a-fA-F]{6}|[0-9a-fA-F]{8}|[0-9a-fA-F]{3,4})$",
|
|
38
48
|
},
|
|
@@ -80,7 +90,7 @@ class MergeParser(BaseParser):
|
|
|
80
90
|
raise ParamValidateException(f"merage操作中actions参数校验异常,其中 {e}")
|
|
81
91
|
|
|
82
92
|
def compute(self, src_w: int, src_h: int, w2: int, h2: int) -> tuple:
|
|
83
|
-
if self.order in enums.PositionOrder:
|
|
93
|
+
if self.order in enums.PositionOrder:
|
|
84
94
|
order = typing.cast(int, self.order)
|
|
85
95
|
w, h, x1, y1, x2, y2 = compute_splice_two_im(
|
|
86
96
|
src_w,
|
|
@@ -107,23 +117,26 @@ class MergeParser(BaseParser):
|
|
|
107
117
|
|
|
108
118
|
def do_action(self, im: Image) -> Image:
|
|
109
119
|
im = pre_processing(im, use_alpha=True)
|
|
110
|
-
src_w, src_h = im.size
|
|
111
120
|
|
|
112
121
|
# 处理要合并的图像
|
|
113
|
-
|
|
114
|
-
|
|
122
|
+
with trans_uri_to_im(self.image, use_copy=True) as _im2:
|
|
123
|
+
im2 = pre_processing(_im2, use_alpha=True)
|
|
124
|
+
|
|
115
125
|
if self.actions:
|
|
116
126
|
from imgprocessor.processor import ProcessorCtr
|
|
117
127
|
|
|
118
128
|
im2 = ProcessorCtr.handle_img_actions(im2, self.actions)
|
|
119
|
-
if self.p:
|
|
120
|
-
w2, h2 = round(src_w * self.p / 100), round(src_h * self.p / 100)
|
|
121
|
-
im2 = im2.resize((w2, h2), resample=Image.LANCZOS)
|
|
122
129
|
|
|
130
|
+
# 调整拼接顺序
|
|
123
131
|
if self.bg:
|
|
124
|
-
# 调整拼接顺序
|
|
125
132
|
im, im2 = im2, im
|
|
126
133
|
|
|
134
|
+
# 缩放图像
|
|
135
|
+
if self.p:
|
|
136
|
+
src_w, src_h = im.size
|
|
137
|
+
w2, h2 = round(src_w * self.p / 100), round(src_h * self.p / 100)
|
|
138
|
+
im2 = im2.resize((w2, h2), resample=Image.LANCZOS)
|
|
139
|
+
|
|
127
140
|
src_w, src_h = im.size
|
|
128
141
|
w2, h2 = im2.size
|
|
129
142
|
|
|
@@ -10,25 +10,29 @@ from .base import BaseParser, pre_processing
|
|
|
10
10
|
|
|
11
11
|
class ResizeParser(BaseParser):
|
|
12
12
|
|
|
13
|
-
KEY = enums.OpAction.RESIZE
|
|
13
|
+
KEY = enums.OpAction.RESIZE.value
|
|
14
14
|
ARGS = {
|
|
15
|
-
"m": {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
15
|
+
"m": {
|
|
16
|
+
"type": enums.ArgType.STRING.value,
|
|
17
|
+
"default": enums.ResizeMode.LFIT.value,
|
|
18
|
+
"choices": enums.ResizeMode,
|
|
19
|
+
},
|
|
20
|
+
"w": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
21
|
+
"h": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
22
|
+
"l": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
23
|
+
"s": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
24
|
+
"limit": {"type": enums.ArgType.INTEGER.value, "default": 1, "choices": [0, 1]},
|
|
21
25
|
"color": {
|
|
22
|
-
"type": enums.ArgType.STRING,
|
|
26
|
+
"type": enums.ArgType.STRING.value,
|
|
23
27
|
"default": "FFFFFF", # 默认白色
|
|
24
28
|
"regex": r"^([0-9a-fA-F]{6}|[0-9a-fA-F]{8}|[0-9a-fA-F]{3,4})$",
|
|
25
29
|
},
|
|
26
|
-
"p": {"type": enums.ArgType.INTEGER, "default": 0, "min": 1, "max": 1000},
|
|
30
|
+
"p": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 1, "max": 1000},
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
def __init__(
|
|
30
34
|
self,
|
|
31
|
-
m: str = enums.ResizeMode.LFIT,
|
|
35
|
+
m: str = enums.ResizeMode.LFIT.value,
|
|
32
36
|
w: int = 0,
|
|
33
37
|
h: int = 0,
|
|
34
38
|
l: int = 0, # noqa: E741
|
|
@@ -50,13 +54,13 @@ class ResizeParser(BaseParser):
|
|
|
50
54
|
def compute(self, src_w: int, src_h: int) -> tuple:
|
|
51
55
|
"""计算出`Image.resize`需要的参数"""
|
|
52
56
|
if self.w or self.h:
|
|
53
|
-
if self.m in [enums.ResizeMode.FIXED, enums.ResizeMode.PAD, enums.ResizeMode.FIT]:
|
|
57
|
+
if self.m in [enums.ResizeMode.FIXED.value, enums.ResizeMode.PAD.value, enums.ResizeMode.FIT.value]:
|
|
54
58
|
# 有可能改变原图宽高比
|
|
55
59
|
if not (self.w and self.h):
|
|
56
60
|
raise ParamValidateException(f"当m={self.m}的模式下,参数w和h都必不可少且不能为0")
|
|
57
61
|
# w,h按指定的即可,无需计算
|
|
58
62
|
w, h = self.w, self.h
|
|
59
|
-
elif self.m == enums.ResizeMode.MFIT:
|
|
63
|
+
elif self.m == enums.ResizeMode.MFIT.value:
|
|
60
64
|
# 低版本Pillow未实现 ImageOps.cover 方法,自行处理
|
|
61
65
|
# 等比缩放
|
|
62
66
|
if self.w and self.h:
|
|
@@ -70,7 +74,7 @@ class ResizeParser(BaseParser):
|
|
|
70
74
|
else:
|
|
71
75
|
w, h = round(self.h * src_w / src_h), self.h
|
|
72
76
|
else:
|
|
73
|
-
# 默认 enums.ResizeMode.LFIT
|
|
77
|
+
# 默认 enums.ResizeMode.LFIT.value
|
|
74
78
|
# 等比缩放
|
|
75
79
|
if self.w and self.h:
|
|
76
80
|
# 指定w与h的矩形内的最大图像
|
|
@@ -114,9 +118,9 @@ class ResizeParser(BaseParser):
|
|
|
114
118
|
if size == im.size:
|
|
115
119
|
# 大小没有变化直接返回
|
|
116
120
|
return im
|
|
117
|
-
if self.m == enums.ResizeMode.PAD:
|
|
121
|
+
if self.m == enums.ResizeMode.PAD.value:
|
|
118
122
|
out = ImageOps.pad(im, size, color=f"#{self.color}")
|
|
119
|
-
elif self.m == enums.ResizeMode.FIT:
|
|
123
|
+
elif self.m == enums.ResizeMode.FIT.value:
|
|
120
124
|
out = ImageOps.fit(im, size)
|
|
121
125
|
else:
|
|
122
126
|
out = im.resize(size, resample=Image.LANCZOS)
|
|
@@ -10,10 +10,10 @@ from .base import BaseParser, pre_processing
|
|
|
10
10
|
|
|
11
11
|
class RotateParser(BaseParser):
|
|
12
12
|
|
|
13
|
-
KEY = enums.OpAction.ROTATE
|
|
13
|
+
KEY = enums.OpAction.ROTATE.value
|
|
14
14
|
ARGS = {
|
|
15
15
|
# 顺时针旋转的度数
|
|
16
|
-
"value": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": 360},
|
|
16
|
+
"value": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": 360},
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
def __init__(
|
|
@@ -6,45 +6,63 @@ from PIL import Image, ImageFont, ImageDraw
|
|
|
6
6
|
|
|
7
7
|
from imgprocessor import enums, settings, utils
|
|
8
8
|
from imgprocessor.exceptions import ParamValidateException
|
|
9
|
-
from .base import
|
|
9
|
+
from .base import (
|
|
10
|
+
BaseParser,
|
|
11
|
+
pre_processing,
|
|
12
|
+
compute_splice_two_im,
|
|
13
|
+
compute_by_geography,
|
|
14
|
+
trans_uri_to_im,
|
|
15
|
+
)
|
|
10
16
|
|
|
11
17
|
|
|
12
18
|
class WatermarkParser(BaseParser):
|
|
13
19
|
|
|
14
|
-
KEY = enums.OpAction.WATERMARK
|
|
20
|
+
KEY = enums.OpAction.WATERMARK.value
|
|
15
21
|
ARGS = {
|
|
16
22
|
# 水印本身的不透明度,100表示完全不透明
|
|
17
|
-
"t": {"type": enums.ArgType.INTEGER, "default": 100, "min": 0, "max": 100},
|
|
18
|
-
"g": {"type": enums.ArgType.STRING, "choices": enums.Geography},
|
|
19
|
-
"x": {"type": enums.ArgType.INTEGER, "default": 10, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
20
|
-
"y": {"type": enums.ArgType.INTEGER, "default": 10, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
23
|
+
"t": {"type": enums.ArgType.INTEGER.value, "default": 100, "min": 0, "max": 100},
|
|
24
|
+
"g": {"type": enums.ArgType.STRING.value, "choices": enums.Geography},
|
|
25
|
+
"x": {"type": enums.ArgType.INTEGER.value, "default": 10, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
26
|
+
"y": {"type": enums.ArgType.INTEGER.value, "default": 10, "min": 0, "max": settings.PROCESSOR_MAX_W_H},
|
|
21
27
|
# percent field, eg: xy
|
|
22
|
-
"pf": {"type": enums.ArgType.STRING, "default": ""},
|
|
28
|
+
"pf": {"type": enums.ArgType.STRING.value, "default": ""},
|
|
23
29
|
# 是否将图片水印或文字水印铺满原图; 值为1开启
|
|
24
|
-
"fill": {"type": enums.ArgType.INTEGER, "default": 0, "choices": [0, 1]},
|
|
25
|
-
"padx": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": 4096},
|
|
26
|
-
"pady": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": 4096},
|
|
30
|
+
"fill": {"type": enums.ArgType.INTEGER.value, "default": 0, "choices": [0, 1]},
|
|
31
|
+
"padx": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": 4096},
|
|
32
|
+
"pady": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": 4096},
|
|
27
33
|
# 图片水印路径
|
|
28
|
-
"image": {"type": enums.ArgType.URI, "base64_encode": True},
|
|
34
|
+
"image": {"type": enums.ArgType.URI.value, "base64_encode": True},
|
|
29
35
|
# 水印的原始设计参照尺寸,会根据原图大小缩放水印
|
|
30
|
-
"design": {"type": enums.ArgType.INTEGER, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
36
|
+
"design": {"type": enums.ArgType.INTEGER.value, "min": 1, "max": settings.PROCESSOR_MAX_W_H},
|
|
31
37
|
# 文字
|
|
32
|
-
"text": {"type": enums.ArgType.STRING, "base64_encode": True, "max_length": 64},
|
|
33
|
-
"font": {"type": enums.ArgType.STRING, "base64_encode": True},
|
|
38
|
+
"text": {"type": enums.ArgType.STRING.value, "base64_encode": True, "max_length": 64},
|
|
39
|
+
"font": {"type": enums.ArgType.STRING.value, "base64_encode": True},
|
|
34
40
|
# 文字默认黑色
|
|
35
|
-
"color": {
|
|
36
|
-
|
|
41
|
+
"color": {
|
|
42
|
+
"type": enums.ArgType.STRING.value,
|
|
43
|
+
"default": "000000",
|
|
44
|
+
"regex": "^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$",
|
|
45
|
+
},
|
|
46
|
+
"size": {"type": enums.ArgType.INTEGER.value, "default": 40, "min": 1, "max": 1000},
|
|
37
47
|
# 文字水印的阴影透明度, 0表示没有阴影
|
|
38
|
-
"shadow": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": 100},
|
|
48
|
+
"shadow": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": 100},
|
|
39
49
|
# 顺时针旋转角度
|
|
40
|
-
"rotate": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": 360},
|
|
50
|
+
"rotate": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": 360},
|
|
41
51
|
# 图文混合水印参数
|
|
42
52
|
# 文字和图片水印的前后顺序; 0表示图片水印在前;1表示文字水印在前
|
|
43
|
-
"order": {
|
|
53
|
+
"order": {
|
|
54
|
+
"type": enums.ArgType.INTEGER.value,
|
|
55
|
+
"default": enums.PositionOrder.BEFORE.value,
|
|
56
|
+
"choices": enums.PositionOrder,
|
|
57
|
+
},
|
|
44
58
|
# 文字水印和图片水印的对齐方式; 0表示文字水印和图片水印上对齐; 1表示文字水印和图片水印中对齐; 2: 表示文字水印和图片水印下对齐
|
|
45
|
-
"align": {
|
|
59
|
+
"align": {
|
|
60
|
+
"type": enums.ArgType.INTEGER.value,
|
|
61
|
+
"default": enums.PositionAlign.BOTTOM.value,
|
|
62
|
+
"choices": enums.PositionAlign,
|
|
63
|
+
},
|
|
46
64
|
# 文字水印和图片水印间的间距
|
|
47
|
-
"interval": {"type": enums.ArgType.INTEGER, "default": 0, "min": 0, "max": 1000},
|
|
65
|
+
"interval": {"type": enums.ArgType.INTEGER.value, "default": 0, "min": 0, "max": 1000},
|
|
48
66
|
}
|
|
49
67
|
|
|
50
68
|
def __init__(
|
|
@@ -100,8 +118,8 @@ class WatermarkParser(BaseParser):
|
|
|
100
118
|
w1, h1, w2, h2 = 0, 0, 0, 0
|
|
101
119
|
icon = None
|
|
102
120
|
if self.image:
|
|
103
|
-
|
|
104
|
-
|
|
121
|
+
with trans_uri_to_im(self.image, use_copy=True) as _icon:
|
|
122
|
+
icon = pre_processing(_icon, use_alpha=True)
|
|
105
123
|
if not self.text:
|
|
106
124
|
# 没有文字,直接返回
|
|
107
125
|
return icon
|
|
@@ -27,12 +27,12 @@ class ProcessorCtr(object):
|
|
|
27
27
|
|
|
28
28
|
def run(self) -> typing.Optional[typing.ByteString]:
|
|
29
29
|
# 初始化输入
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
with trans_uri_to_im(self.input_uri) as ori_im:
|
|
31
|
+
# 处理图像
|
|
32
|
+
im = self.handle_img_actions(ori_im, self.params.actions)
|
|
33
|
+
# 输出、保存
|
|
34
|
+
kwargs = self.params.save_parser.compute(ori_im, im)
|
|
35
|
+
return self.save_img_to_file(im, out_path=self.out_path, **kwargs)
|
|
36
36
|
|
|
37
37
|
@classmethod
|
|
38
38
|
def handle_img_actions(cls, ori_im: Image, actions: list[BaseParser]) -> Image:
|
|
@@ -51,13 +51,12 @@ class ProcessorCtr(object):
|
|
|
51
51
|
**kwargs: typing.Any,
|
|
52
52
|
) -> typing.Optional[typing.ByteString]:
|
|
53
53
|
fmt = kwargs.get("format") or im.format
|
|
54
|
-
kwargs["format"] = fmt
|
|
55
54
|
|
|
56
|
-
if fmt.upper() == enums.ImageFormat.JPEG and im.mode == "RGBA":
|
|
55
|
+
if fmt and fmt.upper() == enums.ImageFormat.JPEG.value and im.mode == "RGBA":
|
|
57
56
|
im = im.convert("RGB")
|
|
58
57
|
|
|
59
58
|
if not kwargs.get("quality"):
|
|
60
|
-
if fmt.upper() == enums.ImageFormat.JPEG and im.format == enums.ImageFormat.JPEG:
|
|
59
|
+
if fmt and fmt.upper() == enums.ImageFormat.JPEG.value and im.format == enums.ImageFormat.JPEG.value:
|
|
61
60
|
kwargs["quality"] = "keep"
|
|
62
61
|
else:
|
|
63
62
|
kwargs["quality"] = settings.PROCESSOR_DEFAULT_QUALITY
|
|
@@ -107,20 +106,20 @@ def extract_main_color(img_path: str, delta_h: float = 0.3) -> str:
|
|
|
107
106
|
颜色值,eg: FFFFFF
|
|
108
107
|
"""
|
|
109
108
|
r, g, b = 0, 0, 0
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
109
|
+
with Image.open(img_path) as im:
|
|
110
|
+
if im.mode != "RGB":
|
|
111
|
+
im = im.convert("RGB")
|
|
112
|
+
# 转换成HSV即 色相(Hue)、饱和度(Saturation)、明度(alue),取值范围[0,1]
|
|
113
|
+
# 取H计算平均色相
|
|
114
|
+
all_h = [colorsys.rgb_to_hsv(*im.getpixel((x, y)))[0] for x in range(im.size[0]) for y in range(im.size[1])]
|
|
115
|
+
avg_h = sum(all_h) / (im.size[0] * im.size[1])
|
|
116
|
+
# 取与平均色相相近的像素色值rgb用于计算,像素值取值范围[0,255]
|
|
117
|
+
beyond = list(
|
|
118
|
+
filter(
|
|
119
|
+
lambda x: abs(colorsys.rgb_to_hsv(*x)[0] - avg_h) < delta_h,
|
|
120
|
+
[im.getpixel((x, y)) for x in range(im.size[0]) for y in range(im.size[1])],
|
|
121
|
+
)
|
|
122
122
|
)
|
|
123
|
-
)
|
|
124
123
|
if len(beyond):
|
|
125
124
|
r = int(sum(e[0] for e in beyond) / len(beyond))
|
|
126
125
|
g = int(sum(e[1] for e in beyond) / len(beyond))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: py-img-processor
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: Image editor using Python and Pillow.
|
|
5
5
|
Home-page: https://github.com/SkylerHu/py-img-processor.git
|
|
6
6
|
Author: SkylerHu
|
|
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
21
21
|
Requires-Python: >=3.9
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
|
-
Requires-Dist: py-enum>=
|
|
24
|
+
Requires-Dist: py-enum>=2.1.1
|
|
25
25
|
Requires-Dist: Pillow>=8
|
|
26
26
|
|
|
27
27
|
# py-img-processor
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{py_img_processor-1.2.0 → py_img_processor-1.2.3}/py_img_processor.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{py_img_processor-1.2.0 → py_img_processor-1.2.3}/py_img_processor.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|