py-img-processor 1.2.3__py3-none-any.whl → 1.3.1__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.
- imgprocessor/__init__.py +3 -1
- imgprocessor/enums.py +0 -4
- imgprocessor/main.py +1 -1
- imgprocessor/parsers/__init__.py +10 -1
- imgprocessor/parsers/base.py +6 -1
- imgprocessor/processor.py +40 -26
- {py_img_processor-1.2.3.dist-info → py_img_processor-1.3.1.dist-info}/METADATA +9 -6
- {py_img_processor-1.2.3.dist-info → py_img_processor-1.3.1.dist-info}/RECORD +12 -12
- {py_img_processor-1.2.3.dist-info → py_img_processor-1.3.1.dist-info}/LICENSE +0 -0
- {py_img_processor-1.2.3.dist-info → py_img_processor-1.3.1.dist-info}/WHEEL +0 -0
- {py_img_processor-1.2.3.dist-info → py_img_processor-1.3.1.dist-info}/entry_points.txt +0 -0
- {py_img_processor-1.2.3.dist-info → py_img_processor-1.3.1.dist-info}/top_level.txt +0 -0
imgprocessor/__init__.py
CHANGED
|
@@ -5,7 +5,7 @@ import importlib
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
__all__ = ["settings", "VERSION"]
|
|
8
|
-
__version__ = "1.
|
|
8
|
+
__version__ = "1.3.1"
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
VERSION = __version__
|
|
@@ -45,6 +45,8 @@ class SettingsProxy(object):
|
|
|
45
45
|
PROCESSOR_WORKSPACES = ()
|
|
46
46
|
# 当资源文件uri使用链接地址时,限制地址域名来源
|
|
47
47
|
PROCESSOR_ALLOW_DOMAINS = ()
|
|
48
|
+
# 临时文件目录
|
|
49
|
+
PROCESSOR_TEMP_DIR = None
|
|
48
50
|
|
|
49
51
|
def __getattribute__(self, attr: str) -> typing.Any:
|
|
50
52
|
try:
|
imgprocessor/enums.py
CHANGED
|
@@ -31,10 +31,6 @@ class ImageOrientation(ChoiceEnum):
|
|
|
31
31
|
class OpAction(ChoiceEnum):
|
|
32
32
|
"""支持的操作类型"""
|
|
33
33
|
|
|
34
|
-
# 以下几个比较特殊,在保存文件时使用
|
|
35
|
-
FORMAT = ("format", "格式")
|
|
36
|
-
QUALITY = ("quality", "质量")
|
|
37
|
-
INTERLACE = ("interlace", "渐进显示")
|
|
38
34
|
# 其他
|
|
39
35
|
RESIZE = ("resize", "缩放")
|
|
40
36
|
CROP = ("crop", "裁剪")
|
imgprocessor/main.py
CHANGED
|
@@ -95,7 +95,7 @@ def main(argv: typing.Optional[list[str]] = None) -> int:
|
|
|
95
95
|
if not os.path.exists(cur_out_dir):
|
|
96
96
|
os.makedirs(cur_out_dir)
|
|
97
97
|
try:
|
|
98
|
-
process_image(file_path,
|
|
98
|
+
process_image(file_path, param_str, out_path=out_path)
|
|
99
99
|
print(f"{tag}\t 成功", flush=True)
|
|
100
100
|
except Exception as e:
|
|
101
101
|
print(f"{tag}\t \033[31m失败:{e}\033[0m", file=sys.stderr, flush=True)
|
imgprocessor/parsers/__init__.py
CHANGED
|
@@ -33,6 +33,14 @@ _ACTION_PARASER_MAP: dict[str, typing.Any] = {
|
|
|
33
33
|
class ProcessParams(object):
|
|
34
34
|
"""图像处理输入参数"""
|
|
35
35
|
|
|
36
|
+
@classmethod
|
|
37
|
+
def init(cls, params: typing.Union["ProcessParams", dict, str]) -> "ProcessParams":
|
|
38
|
+
if isinstance(params, ProcessParams):
|
|
39
|
+
return params
|
|
40
|
+
if isinstance(params, dict):
|
|
41
|
+
return cls(**params)
|
|
42
|
+
return cls.parse_str(params)
|
|
43
|
+
|
|
36
44
|
def __init__(
|
|
37
45
|
self,
|
|
38
46
|
enable_base64: bool = False,
|
|
@@ -66,6 +74,7 @@ class ProcessParams(object):
|
|
|
66
74
|
|
|
67
75
|
save_args = [""] # 加空字符串,是为了保证解析出key
|
|
68
76
|
|
|
77
|
+
save_keys = list(ImgSaveParser.ARGS.keys())
|
|
69
78
|
for item in value.split("/"):
|
|
70
79
|
if not item:
|
|
71
80
|
continue
|
|
@@ -77,7 +86,7 @@ class ProcessParams(object):
|
|
|
77
86
|
key, param_str = info
|
|
78
87
|
if not key:
|
|
79
88
|
raise ParamParseException(f"参数必须指定操作类型 [{item}]不符合参数要求")
|
|
80
|
-
if key in
|
|
89
|
+
if key in save_keys:
|
|
81
90
|
save_args.append(f"{key}_{param_str}")
|
|
82
91
|
else:
|
|
83
92
|
action_cls = _ACTION_PARASER_MAP.get(key)
|
imgprocessor/parsers/base.py
CHANGED
|
@@ -402,7 +402,10 @@ def trans_uri_to_im(uri: str, use_copy: bool = False) -> typing.Generator:
|
|
|
402
402
|
"""
|
|
403
403
|
parsed_url = urllib.parse.urlparse(uri)
|
|
404
404
|
if parsed_url.scheme in _ALLOW_SCHEMES:
|
|
405
|
-
|
|
405
|
+
# 可能包含 %20 (空格) 等编码,需要 unquote
|
|
406
|
+
filename = os.path.basename(urllib.parse.unquote(parsed_url.path))
|
|
407
|
+
_, suffix = os.path.splitext(filename)
|
|
408
|
+
with tempfile.NamedTemporaryFile(suffix=suffix, dir=settings.PROCESSOR_TEMP_DIR) as fp:
|
|
406
409
|
# 输入值计算md5作为文件名;重复地址本地若存在不下载多次
|
|
407
410
|
urlretrieve(uri, filename=fp.name)
|
|
408
411
|
fp.seek(0)
|
|
@@ -430,6 +433,8 @@ def trans_uri_to_im(uri: str, use_copy: bool = False) -> typing.Generator:
|
|
|
430
433
|
|
|
431
434
|
class ImgSaveParser(BaseParser):
|
|
432
435
|
KEY = ""
|
|
436
|
+
|
|
437
|
+
# 定义的key注意和枚举 `OpAction` 中的key不能重复
|
|
433
438
|
ARGS = {
|
|
434
439
|
"format": {"type": enums.ArgType.STRING.value, "default": None},
|
|
435
440
|
"quality": {"type": enums.ArgType.INTEGER.value, "default": None, "min": 1, "max": 100},
|
imgprocessor/processor.py
CHANGED
|
@@ -13,27 +13,6 @@ from imgprocessor.parsers.base import trans_uri_to_im
|
|
|
13
13
|
|
|
14
14
|
class ProcessorCtr(object):
|
|
15
15
|
|
|
16
|
-
def __init__(self, input_uri: str, out_path: str, params: typing.Union[ProcessParams, dict, str]) -> None:
|
|
17
|
-
# 输入文件检验路径
|
|
18
|
-
self.input_uri = BaseParser._validate_uri(input_uri)
|
|
19
|
-
self.out_path = out_path
|
|
20
|
-
# 初始化处理参数
|
|
21
|
-
if isinstance(params, dict):
|
|
22
|
-
params = ProcessParams(**params)
|
|
23
|
-
elif isinstance(params, str):
|
|
24
|
-
params = ProcessParams.parse_str(params)
|
|
25
|
-
params = typing.cast(ProcessParams, params)
|
|
26
|
-
self.params = params
|
|
27
|
-
|
|
28
|
-
def run(self) -> typing.Optional[typing.ByteString]:
|
|
29
|
-
# 初始化输入
|
|
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
|
-
|
|
37
16
|
@classmethod
|
|
38
17
|
def handle_img_actions(cls, ori_im: Image, actions: list[BaseParser]) -> Image:
|
|
39
18
|
im = ori_im
|
|
@@ -68,7 +47,7 @@ class ProcessorCtr(object):
|
|
|
68
47
|
|
|
69
48
|
# 没有传递保存的路径,返回文件内容
|
|
70
49
|
suffix = fmt or "png"
|
|
71
|
-
with tempfile.NamedTemporaryFile(suffix=f".{suffix}") as fp:
|
|
50
|
+
with tempfile.NamedTemporaryFile(suffix=f".{suffix}", dir=settings.PROCESSOR_TEMP_DIR) as fp:
|
|
72
51
|
im.save(fp.name, **kwargs)
|
|
73
52
|
fp.seek(0)
|
|
74
53
|
content = fp.read()
|
|
@@ -76,14 +55,17 @@ class ProcessorCtr(object):
|
|
|
76
55
|
|
|
77
56
|
|
|
78
57
|
def process_image(
|
|
79
|
-
input_uri: str,
|
|
58
|
+
input_uri: str,
|
|
59
|
+
params: typing.Union[ProcessParams, dict, str],
|
|
60
|
+
out_path: typing.Optional[str] = None,
|
|
61
|
+
**kwargs: typing.Any,
|
|
80
62
|
) -> typing.Optional[typing.ByteString]:
|
|
81
63
|
"""处理图像
|
|
82
64
|
|
|
83
65
|
Args:
|
|
84
66
|
input_uri: 输入图像路径
|
|
85
|
-
out_path: 输出图像保存路径
|
|
86
67
|
params: 图像处理参数
|
|
68
|
+
out_path: 输出图像保存路径
|
|
87
69
|
|
|
88
70
|
Raises:
|
|
89
71
|
ProcessLimitException: 超过处理限制会抛出异常
|
|
@@ -91,8 +73,40 @@ def process_image(
|
|
|
91
73
|
Returns:
|
|
92
74
|
默认输出直接存储无返回,仅当out_path为空时会返回处理后图像的二进制内容
|
|
93
75
|
"""
|
|
94
|
-
|
|
95
|
-
|
|
76
|
+
# 初始化输入
|
|
77
|
+
params_obj: ProcessParams = ProcessParams.init(params)
|
|
78
|
+
with trans_uri_to_im(input_uri) as ori_im:
|
|
79
|
+
# 处理图像
|
|
80
|
+
im = ProcessorCtr.handle_img_actions(ori_im, params_obj.actions)
|
|
81
|
+
# 输出、保存
|
|
82
|
+
_kwargs = params_obj.save_parser.compute(ori_im, im)
|
|
83
|
+
_kwargs.update(kwargs)
|
|
84
|
+
ret = ProcessorCtr.save_img_to_file(im, out_path=out_path, **_kwargs)
|
|
85
|
+
return ret
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def process_image_obj(
|
|
89
|
+
ori_im: Image,
|
|
90
|
+
params: typing.Union[ProcessParams, dict, str],
|
|
91
|
+
out_path: typing.Optional[str] = None,
|
|
92
|
+
**kwargs: typing.Any,
|
|
93
|
+
) -> Image:
|
|
94
|
+
"""处理图像
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
ori_im: 输入图像为Image对象
|
|
98
|
+
params: 图像处理参数
|
|
99
|
+
out_path: 输出图像保存路径
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
默认输出直接存储无返回,仅当out_path为空时会返回处理后图像的二进制内容
|
|
103
|
+
"""
|
|
104
|
+
params_obj: ProcessParams = ProcessParams.init(params)
|
|
105
|
+
im = ProcessorCtr.handle_img_actions(ori_im, params_obj.actions)
|
|
106
|
+
_kwargs = params_obj.save_parser.compute(ori_im, im)
|
|
107
|
+
_kwargs.update(kwargs)
|
|
108
|
+
ret = ProcessorCtr.save_img_to_file(im, out_path=out_path, **_kwargs)
|
|
109
|
+
return ret
|
|
96
110
|
|
|
97
111
|
|
|
98
112
|
def extract_main_color(img_path: str, delta_h: float = 0.3) -> str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: py-img-processor
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
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
|
|
@@ -73,6 +73,7 @@ Image editor using Python and Pillow.
|
|
|
73
73
|
| PROCESSOR_TEXT_FONT | str | 默认字体文件,默认从系统中寻找;也可以直接传递字体文件路径 | Arial Unicode.ttf |
|
|
74
74
|
| PROCESSOR_WORKSPACES | tuple | 限制水印等资源路径 (startswith匹配), 默认无限制 | `()` |
|
|
75
75
|
| PROCESSOR_ALLOW_DOMAINS | tuple | 限制链接地址域名 (endswith匹配),默认无限制 | `()` |
|
|
76
|
+
| PROCESSOR_TEMP_DIR | str | tmpfile使用的临时目录,不设置默认使用系统tmp目录 | `None` |
|
|
76
77
|
|
|
77
78
|
> `注意`:`PROCESSOR_TEXT_FONT` 字体的设置是文字水印必要参数,需保证系统已安装该字体。默认值 `Arial Unicode.ttf` 是MacOS系统存在的字体,建议设置字体文件路径。
|
|
78
79
|
|
|
@@ -85,16 +86,18 @@ Image editor using Python and Pillow.
|
|
|
85
86
|
|
|
86
87
|
### 处理函数
|
|
87
88
|
```python
|
|
88
|
-
from imgprocessor.processor import process_image
|
|
89
|
+
from imgprocessor.processor import process_image, process_image_obj
|
|
89
90
|
|
|
90
|
-
process_image(input_uri,
|
|
91
|
+
process_image(input_uri, params, out_path=out_path)
|
|
92
|
+
# 或者
|
|
93
|
+
process_image_obj(im, params, out_path=out_path)
|
|
91
94
|
```
|
|
92
95
|
|
|
93
96
|
参数说明:
|
|
94
97
|
|
|
95
98
|
- `input_uri` str,输入图像文件路径或者链接地址
|
|
96
|
-
- `out_path` str, 输出图像保存路径
|
|
97
99
|
- `params` str or json,图像处理参数,参数说明详见 [Reference.md](https://github.com/SkylerHu/py-img-processor/blob/master/docs/Reference.md)
|
|
100
|
+
- `out_path` str, 输出图像保存路径, 默认为空,为空时返回二进制内容
|
|
98
101
|
|
|
99
102
|
|
|
100
103
|
### 图像处理参数为字符串
|
|
@@ -110,9 +113,9 @@ from imgprocessor.processor import process_image
|
|
|
110
113
|
|
|
111
114
|
process_image(
|
|
112
115
|
"docs/imgs/lenna-400x225.jpg",
|
|
113
|
-
"/tmp/output.png",
|
|
114
116
|
# 对图片缩放、裁剪、生成圆角、并转成png存储
|
|
115
117
|
f"resize,s_200/crop,w_200,h_200,g_center/watermark,text_{base64url_encode('Hello 世界')},color_FFF,size_20/circle,r_10/format,png",
|
|
118
|
+
out_path="/tmp/output.png",
|
|
116
119
|
)
|
|
117
120
|
```
|
|
118
121
|
|
|
@@ -128,7 +131,6 @@ process_image(
|
|
|
128
131
|
```python
|
|
129
132
|
process_image(
|
|
130
133
|
"docs/imgs/lenna-400x225.jpg",
|
|
131
|
-
"/tmp/output.png",
|
|
132
134
|
{
|
|
133
135
|
"actions": [
|
|
134
136
|
{"key": "resize", "s": 200},
|
|
@@ -139,6 +141,7 @@ process_image(
|
|
|
139
141
|
],
|
|
140
142
|
"format": "png",
|
|
141
143
|
},
|
|
144
|
+
out_path="/tmp/output.png",
|
|
142
145
|
)
|
|
143
146
|
```
|
|
144
147
|
该操作与上述字符串示例参数等效。
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
imgprocessor/__init__.py,sha256=
|
|
2
|
-
imgprocessor/enums.py,sha256=
|
|
1
|
+
imgprocessor/__init__.py,sha256=qtGcx7kDmNawsCwjn2HY7nJoQibl63Mrwdk8mD4kEds,2540
|
|
2
|
+
imgprocessor/enums.py,sha256=FEkNzclXiu9Ne0DXuWxlqG3m6q7yENSmlcObnfpDFus,2955
|
|
3
3
|
imgprocessor/exceptions.py,sha256=kj9win_XjCbZnXr93sYs4Cfg1hAJX2fn5hgr1YMNoHQ,384
|
|
4
|
-
imgprocessor/main.py,sha256=
|
|
5
|
-
imgprocessor/processor.py,sha256=
|
|
4
|
+
imgprocessor/main.py,sha256=oBDQJAyx9iC_7xNllsJ6qlSE91Tks0LNJ6O1MzryMoY,4126
|
|
5
|
+
imgprocessor/processor.py,sha256=ocXllGWPiDiSnXEPZqRIU8qNFwomZTJ6_L0m_gTk-fY,4833
|
|
6
6
|
imgprocessor/utils.py,sha256=KBRQJaN8FuuRD2QQPYuuHQ7S-2ML-ctz4rstQTrEdog,1202
|
|
7
|
-
imgprocessor/parsers/__init__.py,sha256=
|
|
7
|
+
imgprocessor/parsers/__init__.py,sha256=NdXhxC_3a74lUDlFelQ3oGAeK_7tJ9_OwfCOmEd-ZvA,3192
|
|
8
8
|
imgprocessor/parsers/alpha.py,sha256=PudXZBu-s5Fzj6fB7Aae4yxJkmdfd_l_oAFSk7279Zk,877
|
|
9
|
-
imgprocessor/parsers/base.py,sha256=
|
|
9
|
+
imgprocessor/parsers/base.py,sha256=5wVUWLDcch2FyFBCYQCORR_2RHYUghCjqtcgpphfGSE,17231
|
|
10
10
|
imgprocessor/parsers/blur.py,sha256=twVLORVV4nXfvHBaqi353X6odOnRi414sye3AOGG5bY,673
|
|
11
11
|
imgprocessor/parsers/circle.py,sha256=VXWgKIkz9emQHWOoO2jbkUFrEbXb1s6ZM-R8BRxSi4c,1665
|
|
12
12
|
imgprocessor/parsers/crop.py,sha256=FfPPmN63XUQZCgRkKuuycj9SLdS-0hnLAU5mRrj-4Uc,3524
|
|
@@ -15,9 +15,9 @@ imgprocessor/parsers/merge.py,sha256=4UIeWwDVg9rJRbTVRvNTyv-mxTA8W5TIgWAvWYXjvhA
|
|
|
15
15
|
imgprocessor/parsers/resize.py,sha256=31iJws5YVsqJZN4O-HG9OjGBtKFW_mGE5536pYvWchU,5175
|
|
16
16
|
imgprocessor/parsers/rotate.py,sha256=Vb23OJuGQrdGRkJWpDJeeVkZUF9bbsRD2e58P5Q1b1k,734
|
|
17
17
|
imgprocessor/parsers/watermark.py,sha256=MTlRVvtVMR5IyJODk5DMFe01KQKGAulaaNy7f23NvME,8862
|
|
18
|
-
py_img_processor-1.
|
|
19
|
-
py_img_processor-1.
|
|
20
|
-
py_img_processor-1.
|
|
21
|
-
py_img_processor-1.
|
|
22
|
-
py_img_processor-1.
|
|
23
|
-
py_img_processor-1.
|
|
18
|
+
py_img_processor-1.3.1.dist-info/LICENSE,sha256=Sr4UxtHWGmoO_enyTUeAN276LkBqJpse0guCcb6f9BQ,1066
|
|
19
|
+
py_img_processor-1.3.1.dist-info/METADATA,sha256=FslQFBULB1AewqEkX80vfFjkacVAngSLAWWWfpq96lw,7810
|
|
20
|
+
py_img_processor-1.3.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
21
|
+
py_img_processor-1.3.1.dist-info/entry_points.txt,sha256=MLeLjzpkH7DkDMgICWCQ99D9ElqAvTBVBGA8yjg4dhQ,57
|
|
22
|
+
py_img_processor-1.3.1.dist-info/top_level.txt,sha256=5Pm26oHcqZoihGGxc5N6qQJ2LuVa2i4au_uqHBMqehI,13
|
|
23
|
+
py_img_processor-1.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|