imgnorm 0.17.8__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.
imgnorm-0.17.8/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zhenzi0322
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,615 @@
1
+ Metadata-Version: 2.4
2
+ Name: imgnorm
3
+ Version: 0.17.8
4
+ Summary: Image normalization utilities - resize images with aspect ratio preservation
5
+ Home-page: https://github.com/zhenzi0322/imgnorm
6
+ Author: zhenzi0322
7
+ Author-email: zhenzi0322 <82131529@qq.com>
8
+ License-Expression: MIT
9
+ Project-URL: Homepage, https://github.com/zhenzi0322/imgnorm
10
+ Project-URL: Repository, https://github.com/zhenzi0322/imgnorm
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Multimedia :: Graphics
14
+ Requires: Pillow
15
+ Requires: requests
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: Pillow>=9.0.0
20
+ Provides-Extra: comfyui
21
+ Requires-Dist: requests>=2.20.0; extra == "comfyui"
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest; extra == "dev"
24
+ Requires-Dist: requests>=2.20.0; extra == "dev"
25
+ Dynamic: author
26
+ Dynamic: home-page
27
+ Dynamic: license-file
28
+ Dynamic: requires
29
+
30
+ ## imgnorm
31
+
32
+ 图片标准化工具 —— 将图片等比缩放并居中放置到指定尺寸的画布上,适用于深度学习数据预处理、图片批量归一化等场景。
33
+
34
+ ## 运行条件
35
+
36
+ * Python >= 3.7
37
+ * Pillow >= 9.0.0
38
+
39
+ ## 安装
40
+
41
+ ```bash
42
+ # 从源码安装(开发模式)
43
+ pip install -e .
44
+
45
+ # 或构建后安装
46
+ pip install .
47
+ ```
48
+
49
+ ## 使用示例
50
+
51
+ ### image_resize — 等比缩放居中
52
+
53
+ ```python
54
+ from PIL import Image
55
+ from imgnorm import image_resize
56
+
57
+ # 打开图片
58
+ img = Image.open("input.jpg")
59
+
60
+ # 缩放到 640x480,默认黑色背景
61
+ result = image_resize(img, 640, 480)
62
+ result.save("output.jpg")
63
+
64
+ # 自定义背景颜色(白色)
65
+ result = image_resize(img, 640, 480, bg_color=(255, 255, 255))
66
+ result.save("output_white.jpg")
67
+ ```
68
+
69
+ ### FluxKontextImageScale — Flux Kontext 优选分辨率缩放
70
+
71
+ ```python
72
+ from PIL import Image
73
+ from imgnorm import FluxKontextImageScale
74
+
75
+ img = Image.open("input.jpg")
76
+
77
+ # 自动匹配最接近原图宽高比的 Flux Kontext 优选分辨率并缩放
78
+ scaler = FluxKontextImageScale(img)
79
+ result = scaler.start()
80
+ result.save("output_kontext.jpg")
81
+ ```
82
+
83
+ ### ImageTool — 多功能比例调整工具
84
+
85
+ ```python
86
+ from imgnorm import ImageTool
87
+
88
+ # 读取图片二进制数据
89
+ with open("input.jpg", "rb") as f:
90
+ image_bytes = f.read()
91
+
92
+ # 自动匹配最接近的标准比例,最小边放大 10% 并填充透明背景
93
+ tool = ImageTool()
94
+ result_bytes = tool.adjust_size(image_bytes)
95
+
96
+ # 指定目标比例 3:4,使用 resize 模式
97
+ tool = ImageTool(proportion="3:4", need_adjust="resize")
98
+ result_bytes = tool.adjust_size(image_bytes)
99
+
100
+ # 指定目标比例 1:1,自定义填充色(白色不透明)
101
+ tool = ImageTool(proportion="1:1", need_adjust="fill", fill_color=(255, 255, 255, 255))
102
+ result_bytes, matched_ratio = tool.adjust_size(image_bytes, return_origin_ratio=True)
103
+ print(f"原图匹配比例: {matched_ratio}")
104
+
105
+ # 保存结果
106
+ with open("output.png", "wb") as f:
107
+ f.write(result_bytes)
108
+ ```
109
+
110
+ ### GPTImageSizeCalculator — gpt-image-2 尺寸计算器
111
+
112
+ ```python
113
+ from imgnorm import GPTImageSizeCalculator
114
+
115
+ # 计算 16:9 比例、2K 分辨率的合法尺寸
116
+ result = GPTImageSizeCalculator.calc_size(aspect_ratio="16:9", resolution="2k")
117
+ print(result)
118
+ # {'width': 2560, 'height': 1440, 'size': '2560x1440', 'pixels': 3686400, ...}
119
+
120
+ # 计算 1:1 比例、4K 分辨率的合法尺寸
121
+ result = GPTImageSizeCalculator.calc_size(aspect_ratio="1:1", resolution="4k")
122
+ print(result['size']) # 2880x2880
123
+
124
+ # 校验尺寸是否合法
125
+ is_valid = GPTImageSizeCalculator.is_valid_image_size(1024, 1024) # True
126
+ ```
127
+
128
+ ### ImageRatioAnalyzer — 图片比例分析器
129
+
130
+ ```python
131
+ from imgnorm import ImageRatioAnalyzer
132
+
133
+ with open("input.jpg", "rb") as f:
134
+ image_bytes = f.read()
135
+
136
+ analyzer = ImageRatioAnalyzer()
137
+
138
+ # 获取精确最简比例(如 1920x1080 → "16:9")
139
+ raw_ratio = analyzer.get_raw_simple_ratio(image_bytes)
140
+ print(f"精确比例: {raw_ratio}")
141
+
142
+ # 获取近似简单整数比例(如 2:3、16:9)
143
+ approx_ratio = analyzer.get_approx_simple_ratio(image_bytes)
144
+ print(f"近似比例: {approx_ratio}")
145
+
146
+ # 调整搜索范围(默认 1~10,增大可匹配更复杂比例)
147
+ analyzer = ImageRatioAnalyzer(approx_search_range=20)
148
+ ```
149
+
150
+ ### ComfyUIManager — ComfyUI 服务管理
151
+
152
+ ```python
153
+ import logging
154
+ from imgnorm import ComfyUIManager
155
+
156
+ # 配置日志(可选)
157
+ logger = logging.getLogger("comfyui")
158
+ logging.basicConfig(level=logging.DEBUG)
159
+
160
+ # 创建管理器(默认连接本地 9131 端口)
161
+ manager = ComfyUIManager(logger=logger)
162
+
163
+ # 发起重启并等待服务恢复
164
+ success = manager.run_reboot_and_wait()
165
+ if success:
166
+ print("服务重启成功")
167
+ else:
168
+ print("服务重启超时")
169
+
170
+ # 自定义配置
171
+ manager = ComfyUIManager(
172
+ base_url="http://192.168.1.100:9131",
173
+ timeout=10,
174
+ max_wait_seconds=120,
175
+ poll_interval=3.0,
176
+ logger=logger
177
+ )
178
+ ```
179
+
180
+ ### PromptBanChecker — 提示词违禁词检测
181
+
182
+ ```python
183
+ from imgnorm import PromptBanChecker, PromptBanError
184
+
185
+ # 初始化(需提供违禁词文件路径,每行一个违禁词)
186
+ checker = PromptBanChecker(ban_word_file="banned_words.txt")
187
+
188
+ # 校验提示词
189
+ try:
190
+ checker.check("some prompt text here")
191
+ print("校验通过")
192
+ except PromptBanError as e:
193
+ print(f"违禁词拦截: {e.error_msg}")
194
+ print(f"命中词: {e.extend_data}")
195
+
196
+ # 查找所有命中词(不抛异常)
197
+ hits = checker.find_matched_words("text to check")
198
+ print(f"命中: {hits}")
199
+
200
+ # 重新加载违禁词文件
201
+ checker.load_banned_words()
202
+ ```
203
+
204
+ ### ErrorCode + ServiceResult — 统一错误码与响应
205
+
206
+ ```python
207
+ from imgnorm import ErrorCode, ServiceResult, ResultStatus
208
+
209
+ # 构建成功响应
210
+ result = ServiceResult.success(data={"image_url": "https://..."})
211
+ print(result.to_dict())
212
+
213
+ # 构建失败响应(使用 ErrorCode 默认消息)
214
+ result = ServiceResult.fail(ErrorCode.SERVICE_BUSY)
215
+ print(result.to_dict())
216
+
217
+ # 构建失败响应(自定义消息覆盖)
218
+ result = ServiceResult.fail(ErrorCode.IMAGE_GENERATE_FAIL, msg="自定义错误信息")
219
+ print(result.to_dict())
220
+
221
+ # 自动识别上游错误并替换为友好提示
222
+ result = ServiceResult.fail(
223
+ ErrorCode.IMAGE_GENERATE_FAIL,
224
+ msg="Upstream model timed out. Try again later."
225
+ )
226
+ # msg 自动替换为 "服务超时,请稍后重试"
227
+
228
+ # 动态注入自定义错误匹配规则
229
+ # 格式1:(关键词列表, ErrorCode) — 匹配时替换为 ErrorCode 默认消息
230
+ # 格式2:(关键词列表, ErrorCode, 自定义消息) — 匹配时替换为自定义消息
231
+ ServiceResult._error_rules = [
232
+ (["insufficient balance", "余额不足"], ErrorCode.INSUFFICIENT_BALANCE),
233
+ (["upstream model timed out"], ErrorCode.TIMEOUT),
234
+ (["no image", "未返回图片"], ErrorCode.NO_IMAGE_RESULT, "未返回图片"),
235
+ ]
236
+ result = ServiceResult.fail(ErrorCode.INSUFFICIENT_BALANCE, msg='no image xxx')
237
+ print(result.to_dict())
238
+ ```
239
+
240
+ ### CustomErrorCode — 自定义错误码扩展
241
+
242
+ ```python
243
+ from imgnorm import CustomErrorCode, ErrorCodeRegistry, ServiceResult
244
+
245
+ # 方式一:直接创建自定义错误码
246
+ MY_ERROR = CustomErrorCode("MY_ERROR", "自定义错误消息")
247
+ result = ServiceResult.fail(MY_ERROR)
248
+
249
+ # 方式二:通过注册表注册(可全局查找)
250
+ RATE_LIMIT = ErrorCodeRegistry.register("RATE_LIMIT", "请求过于频繁,请稍后再试")
251
+ result = ServiceResult.fail(RATE_LIMIT)
252
+
253
+ # 查找已注册的错误码
254
+ found = ErrorCodeRegistry.get("RATE_LIMIT")
255
+
256
+ # 列出所有已注册的错误码
257
+ all_codes = ErrorCodeRegistry.list_all()
258
+ ```
259
+
260
+ ### str_to_md5 — 字符串转 MD5
261
+
262
+ ```python
263
+ from imgnorm import str_to_md5
264
+
265
+ # 字符串转 MD5
266
+ hash_str = str_to_md5("hello world")
267
+ print(hash_str) # 5eb63bbbe01eeed093cb22bb8f5acdc3
268
+
269
+ # bytes 输入
270
+ hash_bytes = str_to_md5(b"binary data")
271
+ ```
272
+
273
+ ### is_valid_image — 图片有效性检测
274
+
275
+ ```python
276
+ from imgnorm import is_valid_image, InvalidImageError
277
+
278
+ with open("input.jpg", "rb") as f:
279
+ image_bytes = f.read()
280
+
281
+ # 传递额外上下文(如 task_id)
282
+ try:
283
+ is_valid_image(image_bytes, task_id="abc123", user_id=42)
284
+ print("图片有效")
285
+ except InvalidImageError as e:
286
+ print(f"图片损坏: {e}")
287
+ print(f"文件大小: {e.image_size} bytes")
288
+ print(f"任务ID: {e.extra_data.get('task_id')}") # abc123
289
+ print(f"用户ID: {e.extra_data.get('user_id')}") # 42
290
+ ```
291
+
292
+ ## API 说明
293
+
294
+ ### `image_resize(image, target_w, target_h, bg_color=(0, 0, 0))`
295
+
296
+ 将图片等比缩放并居中放置到指定尺寸的画布上。
297
+
298
+ | 参数 | 类型 | 说明 |
299
+ |------|------|------|
300
+ | `image` | `PIL.Image.Image` | 输入图片对象 |
301
+ | `target_w` | `int` | 目标宽度 |
302
+ | `target_h` | `int` | 目标高度 |
303
+ | `bg_color` | `tuple` | 画布背景颜色(RGB 元组),默认黑色 `(0, 0, 0)` |
304
+
305
+ **返回值:** 缩放并居中后的 `PIL.Image.Image` 对象。
306
+
307
+ **处理逻辑:**
308
+ 1. 创建指定尺寸和背景色的画布
309
+ 2. 计算等比缩放比例(取宽、高缩放比的最小值),确保图片完整放入画布
310
+ 3. 使用 LANCZOS 算法高质量缩放图片
311
+ 4. 计算居中偏移量,将缩放后的图片粘贴到画布中央
312
+
313
+ ### `FluxKontextImageScale(image)`
314
+
315
+ 根据原图宽高比,将图片缩放到最接近的 Flux Kontext 优选分辨率。
316
+
317
+ | 参数 | 类型 | 说明 |
318
+ |------|------|------|
319
+ | `image` | `PIL.Image.Image` | 输入图片对象 |
320
+
321
+ **方法 `start()`:** 计算原图宽高比,从 17 个优选分辨率中匹配最接近的一个,使用 LANCZOS 算法缩放并返回结果图片。
322
+
323
+ **优选分辨率列表(宽×高):**
324
+ `672×1568` · `688×1504` · `720×1456` · `752×1392` · `800×1328` · `832×1248` · `880×1184` · `944×1104` · `1024×1024` · `1104×944` · `1184×880` · `1248×832` · `1328×800` · `1392×752` · `1456×720` · `1504×688` · `1568×672`
325
+
326
+ **返回值:** 缩放后的 `PIL.Image.Image` 对象。
327
+
328
+ ### `ImageTool(proportion=None, need_adjust="fill", fill_color=(255,255,255,0))`
329
+
330
+ 多功能图片尺寸调整工具,支持标准比例匹配和两种调整模式。
331
+
332
+ | 参数 | 类型 | 说明 |
333
+ |------|------|------|
334
+ | `proportion` | `str \| None` | 目标比例,如 `"3:4"` / `"1:1"`;空字符串或 `None` 表示自动匹配 |
335
+ | `need_adjust` | `str` | 调整模式:`"fill"`(画布填充)或 `"resize"`(拉伸) |
336
+ | `fill_color` | `tuple` | 填充背景色 RGBA,默认透明 `(255, 255, 255, 0)` |
337
+
338
+ **方法 `adjust_size(image_bytes, return_origin_ratio=False)`:**
339
+
340
+ | 参数 | 类型 | 说明 |
341
+ |------|------|------|
342
+ | `image_bytes` | `bytes` | 图片二进制数据 |
343
+ | `return_origin_ratio` | `bool` | 是否同时返回原图匹配的标准比例字符串 |
344
+
345
+ **返回值:** `bytes`(处理后图片数据);当 `return_origin_ratio=True` 时返回 `(bytes, str)`。
346
+
347
+ **标准比例池:** `1:1` · `2:3` · `3:2` · `3:4` · `4:3` · `4:5` · `5:4` · `9:16` · `16:9` · `21:9`
348
+
349
+ **处理逻辑:**
350
+ 1. `proportion` 存在且与原图最接近标准比例不一致 → 直接返回原图
351
+ 2. `proportion` 存在且一致 → 反转比例后执行 resize/fill
352
+ 3. `proportion` 为空且 `need_adjust="fill"` → 最小边放大 10%,画布填充
353
+ 4. `proportion` 为空且 `need_adjust="resize"` → 单边放大 10% 后拉伸
354
+
355
+ ### `GPTImageSizeCalculator`
356
+
357
+ gpt-image-2 尺寸计算器,根据比例和分辨率自动生成合法图片尺寸。
358
+
359
+ **类方法:**
360
+
361
+ | 方法 | 说明 |
362
+ |------|------|
363
+ | `calc_size(aspect_ratio="1:1", resolution="2k")` | 根据比例和分辨率计算合法尺寸 |
364
+ | `is_valid_image_size(width, height)` | 校验尺寸是否合法 |
365
+ | `round_to_multiple(value, multiple=16)` | 四舍五入到指定倍数 |
366
+
367
+ **`calc_size` 参数:**
368
+
369
+ | 参数 | 类型 | 说明 |
370
+ |------|------|------|
371
+ | `aspect_ratio` | `str` | 宽高比,如 `"1:1"` / `"16:9"` / `"4:3"` |
372
+ | `resolution` | `str` | 分辨率档位:`"1k"` / `"2k"` / `"4k"` |
373
+
374
+ **返回值:** `dict`,包含 `width`、`height`、`size`、`pixels`、`aspect_ratio`、`resolution`、`valid`。
375
+
376
+ **合法性约束:**
377
+ - 最大边 ≤ 3840
378
+ - 宽高为 16 的倍数
379
+ - 宽高比 ≤ 3:1
380
+ - 像素范围:655,360 ~ 8,294,400
381
+
382
+ ### `ImageRatioAnalyzer(approx_search_range=10)`
383
+
384
+ 图片比例分析工具,获取图片的宽高比信息。
385
+
386
+ | 参数 | 类型 | 说明 |
387
+ |------|------|------|
388
+ | `approx_search_range` | `int` | 近似比例搜索整数范围,默认 1~10 |
389
+
390
+ **方法:**
391
+
392
+ | 方法 | 说明 |
393
+ |------|------|
394
+ | `get_raw_simple_ratio(image_bytes)` | 获取精确最简宽高比(GCD 约分) |
395
+ | `get_approx_simple_ratio(image_bytes)` | 自动拟合近似简单整数比例 |
396
+
397
+ **`get_raw_simple_ratio`:** 通过最大公约数约分,返回精确的最简比例字符串,如 `1920x1080` → `"16:9"`。
398
+
399
+ **`get_approx_simple_ratio`:** 遍历 `1~approx_search_range` 范围内的整数组合,找到与图片实际比例最接近的简单整数比,返回如 `"2:3"`、`"16:9"`。
400
+
401
+ **返回值:** 比例字符串(`str`)。
402
+
403
+ ### `ComfyUIManager`
404
+
405
+ ComfyUI 服务管理工具,位于 `imgnorm.comfyui` 子模块。
406
+
407
+ | 参数 | 类型 | 说明 |
408
+ |------|------|------|
409
+ | `base_url` | `str` | 服务地址,默认 `"http://127.0.0.1:9131"` |
410
+ | `timeout` | `int` | 请求超时秒数,默认 5 |
411
+ | `max_wait_seconds` | `int` | 最长等待恢复时间,默认 60 |
412
+ | `poll_interval` | `float` | 轮询间隔秒数,默认 2.0 |
413
+ | `logger` | `logging.Logger` | 日志记录器,默认使用模块 logger |
414
+
415
+ **方法:**
416
+
417
+ | 方法 | 说明 |
418
+ |------|------|
419
+ | `trigger_reboot()` | 发起重启请求,远程断开视为成功 |
420
+ | `wait_service_ready()` | 轮询检测服务是否恢复,超时返回 False |
421
+ | `run_reboot_and_wait()` | 完整流程:重启 + 等待,返回 bool |
422
+
423
+ **工作原理:**
424
+ 1. 调用 `/api/manager/reboot` 接口触发重启
425
+ 2. 轮询 `/api/system_stats` 接口,状态码 200 判定恢复
426
+ 3. 超时未恢复返回 `False`
427
+
428
+ **安装:** 使用 comfyui 功能需额外安装 `requests`:
429
+ ```bash
430
+ pip install imgnorm[comfyui]
431
+ # 或
432
+ pip install requests>=2.20.0
433
+ ```
434
+
435
+ ### `PromptBanChecker(ban_word_file, ignore_case=True)`
436
+
437
+ 提示词违禁词校验工具,从文件加载违禁词列表,使用正则预编译实现高效匹配。
438
+
439
+ | 参数 | 类型 | 说明 |
440
+ |------|------|------|
441
+ | `ban_word_file` | `str` | 违禁词文件路径(每行一个词) |
442
+ | `ignore_case` | `bool` | 是否忽略大小写,默认 `True` |
443
+
444
+ **方法:**
445
+
446
+ | 方法 | 说明 |
447
+ |------|------|
448
+ | `check(prompt)` | 校验提示词,命中则抛出 `PromptBanError` |
449
+ | `find_matched_words(text)` | 查找所有命中词,返回列表 |
450
+ | `load_banned_words()` | 重新加载违禁词文件 |
451
+
452
+ ### `PromptBanError`
453
+
454
+ 违禁词拦截异常,继承自 `Exception`。
455
+
456
+ | 属性 | 类型 | 说明 |
457
+ |------|------|------|
458
+ | `error_msg` | `str` | 错误描述 |
459
+ | `extend_data` | `Any` | 命中的违禁词列表 |
460
+
461
+ ```python
462
+ from imgnorm import PromptBanError
463
+ # 或
464
+ from imgnorm.exceptions import PromptBanError
465
+ ```
466
+
467
+ ### `InvalidImageError`
468
+
469
+ 图片无效异常,继承自 `Exception`。当图片损坏、格式错误或无法解码时抛出。
470
+
471
+ | 属性 | 类型 | 说明 |
472
+ |------|------|------|
473
+ | `error_msg` | `str` | 错误描述 |
474
+ | `image_size` | `int` | 图片数据大小(字节数) |
475
+ | `original_error` | `Exception` | 原始异常 |
476
+ | `extra_data` | `dict` | 额外上下文数据(如 task_id 等) |
477
+
478
+ ```python
479
+ from imgnorm import InvalidImageError
480
+ # 或
481
+ from imgnorm.exceptions import InvalidImageError
482
+ ```
483
+
484
+ ### `ErrorCode`
485
+
486
+ 统一错误码枚举,位于 `imgnorm.response`。
487
+
488
+ **通用:**
489
+
490
+ | 成员 | code | 默认消息 |
491
+ |------|------|----------|
492
+ | `SUCCESS` | `"SUCCESS"` | 服务完成 |
493
+ | `SERVICE_BUSY` | `"SERVICE_BUSY"` | 服务繁忙,请稍后再试 |
494
+ | `TIMEOUT` | `"TIMEOUT"` | 服务超时,请稍后重试 |
495
+
496
+ **图片生成:**
497
+
498
+ | 成员 | code | 默认消息 |
499
+ |------|------|----------|
500
+ | `IMAGE_GENERATE_FAIL` | `"IMAGE_GENERATE_FAIL"` | 图片生成失败,请稍后重试 |
501
+ | `NO_IMAGE_RESULT` | `"NO_IMAGE_RESULT"` | 模型未返回图片结果,请调整图像或提示词后重试 |
502
+
503
+ **图片输入:**
504
+
505
+ | 成员 | code | 默认消息 |
506
+ |------|------|----------|
507
+ | `IMAGE_INVALID` | `"IMAGE_INVALID"` | 图片无效或已损坏,请上传有效的图片 |
508
+ | `IMAGE_FORMAT_UNSUPPORTED` | `"IMAGE_FORMAT_UNSUPPORTED"` | 不支持的图片格式 |
509
+ | `IMAGE_SIZE_EXCEEDED` | `"IMAGE_SIZE_EXCEEDED"` | 图片尺寸超出限制,请调整图片大小后重试 |
510
+
511
+ **内容安全:**
512
+
513
+ | 成员 | code | 默认消息 |
514
+ |------|------|----------|
515
+ | `CONTENT_POLICY_VIOLATION` | `"CONTENT_POLICY_VIOLATION"` | 您的请求包含违反平台政策的内容,请调整图像或提示词后重试 |
516
+ | `IMAGE_BANNED` | `"IMAGE_BANNED"` | 图片包含违禁内容,请更换图片后重试 |
517
+ | `PROMPT_BANNED` | `"PROMPT_BANNED"` | 提示词包含违禁内容,请修改后重试 |
518
+
519
+ **提示词:**
520
+
521
+ | 成员 | code | 默认消息 |
522
+ |------|------|----------|
523
+ | `PROMPT_EMPTY` | `"PROMPT_EMPTY"` | 提示词不能为空 |
524
+ | `PROMPT_TOO_LONG` | `"PROMPT_TOO_LONG"` | 提示词超出长度限制,请精简后重试 |
525
+
526
+ **参数:**
527
+
528
+ | 成员 | code | 默认消息 |
529
+ |------|------|----------|
530
+ | `INVALID_PARAMS` | `"INVALID_PARAMS"` | 请求参数无效,请检查后重试 |
531
+
532
+ **账户/配额:**
533
+
534
+ | 成员 | code | 默认消息 |
535
+ |------|------|----------|
536
+ | `INSUFFICIENT_BALANCE` | `"INSUFFICIENT_BALANCE"` | 服务API余额不足,联系客服充值后重试 |
537
+ | `QUOTA_EXCEEDED` | `"QUOTA_EXCEEDED"` | 请求配额已用完,请稍后重试或升级套餐 |
538
+ | `RATE_LIMIT` | `"RATE_LIMIT"` | 请求过于频繁,请稍后再试 |
539
+ | `AUTH_FAILED` | `"AUTH_FAILED"` | 认证失败,请检查 API Key 是否有效 |
540
+
541
+ **属性:** `.code`(错误标识)、`.msg`(用户可读描述)。
542
+
543
+ ### `ServiceResult`
544
+
545
+ 统一接口返回类,位于 `imgnorm.response`。
546
+
547
+ | 字段 | 类型 | 默认值 | 说明 |
548
+ |------|------|--------|------|
549
+ | `status` | `str` | `"wait"` | 状态:success/fail/wait |
550
+ | `data` | `Any` | `None` | 返回数据 |
551
+ | `code` | `str` | `"SUCCESS"` | 错误码 |
552
+ | `msg` | `str` | `""` | 提示信息 |
553
+ | `status_code` | `int` | `400` | HTTP 状态码 |
554
+
555
+ **类方法:**
556
+
557
+ | 方法 | 说明 |
558
+ |------|------|
559
+ | `ServiceResult.success(data, msg)` | 快速构建成功响应 |
560
+ | `ServiceResult.fail(error, msg, status_code)` | 快速构建失败响应 |
561
+ | `result.to_dict()` | 转为字典 |
562
+
563
+ **动态错误规则:** 通过 `ServiceResult._error_rules` 注入自定义匹配规则:
564
+ - `(关键词列表, ErrorCode)` — 匹配时替换为 ErrorCode 默认消息
565
+ - `(关键词列表, ErrorCode, 自定义消息)` — 匹配时替换为自定义消息
566
+
567
+ ### `ResultStatus`
568
+
569
+ 返回状态枚举:`SUCCESS` / `FAIL` / `WAIT`。
570
+
571
+ ### `CustomErrorCode(code, msg)`
572
+
573
+ 自定义错误码类,用于扩展内置 `ErrorCode` 枚举。
574
+
575
+ | 参数 | 类型 | 说明 |
576
+ |------|------|------|
577
+ | `code` | `str` | 错误码标识字符串 |
578
+ | `msg` | `str` | 用户可读的错误描述 |
579
+
580
+ **属性:** `.code`、`.msg`。可直接传给 `ServiceResult.fail()` 使用。
581
+
582
+ ### `ErrorCodeRegistry`
583
+
584
+ 错误码注册表,提供全局注册和查找自定义错误码的功能。
585
+
586
+ | 方法 | 说明 |
587
+ |------|------|
588
+ | `register(code, msg)` | 注册并返回 CustomErrorCode 实例 |
589
+ | `get(code)` | 根据 code 查找已注册的错误码 |
590
+ | `list_all()` | 返回所有已注册的错误码字典 |
591
+ | `clear()` | 清空所有注册的错误码 |
592
+
593
+ ### `str_to_md5(content)`
594
+
595
+ 字符串转 MD5 哈希值。
596
+
597
+ | 参数 | 类型 | 说明 |
598
+ |------|------|------|
599
+ | `content` | `str \| bytes` | 待转换的字符串或 bytes |
600
+
601
+ **返回值:** 32 位小写 MD5 十六进制字符串。
602
+
603
+ ### `is_valid_image(image_bytes, **kwargs)`
604
+
605
+ 判断图片是否有效(未损坏)。
606
+
607
+ | 参数 | 类型 | 说明 |
608
+ |------|------|------|
609
+ | `image_bytes` | `bytes` | 图片二进制数据 |
610
+ | `**kwargs` | `Any` | 额外上下文数据,透传到 `InvalidImageError.extra_data` |
611
+
612
+ **返回值:** `True` 表示图片有效。
613
+
614
+ **异常:** 图片损坏时抛出 `InvalidImageError`,包含 `image_size` 和 `extra_data` 属性。
615
+