TheEeeeLin commited on
Commit
f8cafb8
1 Parent(s): cb449af
assets/title.md CHANGED
@@ -1,11 +1,11 @@
1
- <div style="display: flex; justify-content: center; align-items: center; text-align: center; font-size: 40px;">
2
- <div>
3
  <b>HivisionIDPhotos</b>
4
- <br>
5
- <div style="display: flex; justify-content: center; align-items: center; text-align: center;">
6
- <a href="https://github.com/xiaolin199912/HivisionIDPhotos"><img alt="Github" src="https://img.shields.io/static/v1?label=GitHub&message=GitHub&color=black"></a> &ensp;
7
- <a href="https://docs.qq.com/doc/DUkpBdk90eWZFS2JW" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/WeChat-微信-4cb55e"></a> &ensp;
8
- <a href="https://github.com/Zeyi-Lin/HivisionIDPhotos/blob/master/docs/api_EN.md" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/API_Docs-API文档-315bce"></a>
9
- </div>
10
  </div>
11
- </div>
 
 
 
 
 
 
 
1
+ <div style="display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; font-size: 40px;">
2
+ <div style="display: flex; align-items: center;">
3
  <b>HivisionIDPhotos</b>
 
 
 
 
 
 
4
  </div>
5
+ <div style="display: flex; justify-content: center; align-items: center; text-align: center;">
6
+ <a href="https://github.com/xiaolin199912/HivisionIDPhotos"><img alt="Github" src="https://img.shields.io/static/v1?label=GitHub&message=GitHub&color=black"></a> &ensp;
7
+ <a href="https://docs.qq.com/doc/DUkpBdk90eWZFS2JW" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/WeChat-微信-4cb55e"></a> &ensp;
8
+ <a href="https://github.com/xiaolin199912/HivisionIDPhotos/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/zeyi-lin/hivisionidphotos?color=ffcb47&labelColor=black&style=flat-square"></a> &ensp;
9
+ <a href="https://github.com/Zeyi-Lin/HivisionIDPhotos/blob/master/docs/api_EN.md" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/API_Docs-API文档-315bce"></a>
10
+ </div>
11
+ </div>
demo/locals.py CHANGED
@@ -35,6 +35,12 @@ LOCALES = {
35
  "zh": {
36
  "label": "人脸检测模型",
37
  },
 
 
 
 
 
 
38
  },
39
  "matting_model": {
40
  "en": {
@@ -43,6 +49,12 @@ LOCALES = {
43
  "zh": {
44
  "label": "抠图模型",
45
  },
 
 
 
 
 
 
46
  },
47
  "key_param": {
48
  "en": {
@@ -51,6 +63,12 @@ LOCALES = {
51
  "zh": {
52
  "label": "核心参数",
53
  },
 
 
 
 
 
 
54
  },
55
  "advance_param": {
56
  "en": {
@@ -59,6 +77,12 @@ LOCALES = {
59
  "zh": {
60
  "label": "高级参数",
61
  },
 
 
 
 
 
 
62
  },
63
  "size_mode": {
64
  "en": {
@@ -71,6 +95,20 @@ LOCALES = {
71
  "choices": ["尺寸列表", "只换底", "自定义尺寸"],
72
  "custom_size_eror": "宽度不应大于长度;长度和宽度不应小于100,不大于1800。",
73
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  },
75
  "size_list": {
76
  "en": {
@@ -83,6 +121,16 @@ LOCALES = {
83
  "choices": list(size_list_dict_CN.keys()),
84
  "develop": size_list_config_CN,
85
  },
 
 
 
 
 
 
 
 
 
 
86
  },
87
  "bg_color": {
88
  "en": {
@@ -95,6 +143,16 @@ LOCALES = {
95
  "choices": list(color_list_dict_CN.keys()) + ["自定义底色"],
96
  "develop": color_list_dict_CN,
97
  },
 
 
 
 
 
 
 
 
 
 
98
  },
99
  "button": {
100
  "en": {
@@ -103,6 +161,12 @@ LOCALES = {
103
  "zh": {
104
  "label": "开始制作",
105
  },
 
 
 
 
 
 
106
  },
107
  "head_measure_ratio": {
108
  "en": {
@@ -111,6 +175,12 @@ LOCALES = {
111
  "zh": {
112
  "label": "面部比例",
113
  },
 
 
 
 
 
 
114
  },
115
  "top_distance": {
116
  "en": {
@@ -119,6 +189,12 @@ LOCALES = {
119
  "zh": {
120
  "label": "头距顶距离",
121
  },
 
 
 
 
 
 
122
  },
123
  "image_kb": {
124
  "en": {
@@ -129,6 +205,14 @@ LOCALES = {
129
  "label": "设置 KB 大小",
130
  "choices": ["不设置", "自定义"],
131
  },
 
 
 
 
 
 
 
 
132
  },
133
  "image_kb_size": {
134
  "en": {
@@ -137,6 +221,44 @@ LOCALES = {
137
  "zh": {
138
  "label": "KB 大小",
139
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  },
141
  "render_mode": {
142
  "en": {
@@ -151,6 +273,22 @@ LOCALES = {
151
  "label": "渲染方式",
152
  "choices": ["纯色", "上下渐变(白色)", "中心渐变(白色)"],
153
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  },
155
  # Tab3 - 水印工作台
156
  "watermark_tab": {
@@ -160,6 +298,12 @@ LOCALES = {
160
  "zh": {
161
  "label": "水印",
162
  },
 
 
 
 
 
 
163
  },
164
  "watermark_text": {
165
  "en": {
@@ -172,6 +316,16 @@ LOCALES = {
172
  "value": "Hello",
173
  "placeholder": "最多20个字符",
174
  },
 
 
 
 
 
 
 
 
 
 
175
  },
176
  "watermark_color": {
177
  "en": {
@@ -180,6 +334,12 @@ LOCALES = {
180
  "zh": {
181
  "label": "水印颜色",
182
  },
 
 
 
 
 
 
183
  },
184
  "watermark_size": {
185
  "en": {
@@ -188,6 +348,12 @@ LOCALES = {
188
  "zh": {
189
  "label": "文字大小",
190
  },
 
 
 
 
 
 
191
  },
192
  "watermark_opacity": {
193
  "en": {
@@ -196,6 +362,12 @@ LOCALES = {
196
  "zh": {
197
  "label": "水印透明度",
198
  },
 
 
 
 
 
 
199
  },
200
  "watermark_angle": {
201
  "en": {
@@ -204,6 +376,12 @@ LOCALES = {
204
  "zh": {
205
  "label": "水印角度",
206
  },
 
 
 
 
 
 
207
  },
208
  "watermark_space": {
209
  "en": {
@@ -212,6 +390,12 @@ LOCALES = {
212
  "zh": {
213
  "label": "水印间距",
214
  },
 
 
 
 
 
 
215
  },
216
  "watermark_switch": {
217
  "en": {
@@ -224,6 +408,16 @@ LOCALES = {
224
  "value": "不添加",
225
  "choices": ["不添加", "添加"],
226
  },
 
 
 
 
 
 
 
 
 
 
227
  },
228
  # 输出结果
229
  "notification": {
@@ -235,6 +429,14 @@ LOCALES = {
235
  "label": "通知",
236
  "face_error": "人脸数不等于1,请上传单人照片。如果实际人脸数为1,可能是检测模型的准确度问题,请切换左侧不同的人脸检测模型或提出Github Issue通知作者。",
237
  },
 
 
 
 
 
 
 
 
238
  },
239
  "standard_photo": {
240
  "en": {
@@ -243,6 +445,12 @@ LOCALES = {
243
  "zh": {
244
  "label": "标准照",
245
  },
 
 
 
 
 
 
246
  },
247
  "hd_photo": {
248
  "en": {
@@ -251,6 +459,12 @@ LOCALES = {
251
  "zh": {
252
  "label": "高清照",
253
  },
 
 
 
 
 
 
254
  },
255
  "standard_photo_png": {
256
  "en": {
@@ -259,6 +473,12 @@ LOCALES = {
259
  "zh": {
260
  "label": "透明标准照",
261
  },
 
 
 
 
 
 
262
  },
263
  "hd_photo_png": {
264
  "en": {
@@ -267,6 +487,12 @@ LOCALES = {
267
  "zh": {
268
  "label": "透明高清照",
269
  },
 
 
 
 
 
 
270
  },
271
  "layout_photo": {
272
  "en": {
@@ -275,13 +501,25 @@ LOCALES = {
275
  "zh": {
276
  "label": "六寸排版照",
277
  },
 
 
 
 
 
 
278
  },
279
  "download": {
280
  "en": {
281
- "label": "Download the photo after adjusting the KB size",
282
  },
283
  "zh": {
284
- "label": "下载调整 KB 大小后的照片",
 
 
 
 
 
 
285
  },
286
  },
287
  "matting_image": {
@@ -291,6 +529,12 @@ LOCALES = {
291
  "zh": {
292
  "label": "抠图图像",
293
  },
 
 
 
 
 
 
294
  },
295
  "beauty_tab": {
296
  "en": {
@@ -299,6 +543,12 @@ LOCALES = {
299
  "zh": {
300
  "label": "美颜",
301
  },
 
 
 
 
 
 
302
  },
303
  "whitening_strength": {
304
  "en": {
@@ -307,5 +557,67 @@ LOCALES = {
307
  "zh": {
308
  "label": "美白强度",
309
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  },
311
  }
 
35
  "zh": {
36
  "label": "人脸检测模型",
37
  },
38
+ "ja": {
39
+ "label": "顔検出モデル",
40
+ },
41
+ "ko": {
42
+ "label": "얼굴 감지 모델",
43
+ },
44
  },
45
  "matting_model": {
46
  "en": {
 
49
  "zh": {
50
  "label": "抠图模型",
51
  },
52
+ "ja": {
53
+ "label": "マッティングモデル",
54
+ },
55
+ "ko": {
56
+ "label": "매팅 모델",
57
+ },
58
  },
59
  "key_param": {
60
  "en": {
 
63
  "zh": {
64
  "label": "核心参数",
65
  },
66
+ "ja": {
67
+ "label": "主要パラメータ",
68
+ },
69
+ "ko": {
70
+ "label": "주요 매개변수",
71
+ },
72
  },
73
  "advance_param": {
74
  "en": {
 
77
  "zh": {
78
  "label": "高级参数",
79
  },
80
+ "ja": {
81
+ "label": "詳細パラメータ",
82
+ },
83
+ "ko": {
84
+ "label": "고급 매개변수",
85
+ },
86
  },
87
  "size_mode": {
88
  "en": {
 
95
  "choices": ["尺寸列表", "只换底", "自定义尺寸"],
96
  "custom_size_eror": "宽度不应大于长度;长度和宽度不应小于100,不大于1800。",
97
  },
98
+ "ja": {
99
+ "label": "証明写真サイズオプション",
100
+ "choices": [
101
+ "サイズリスト",
102
+ "背景のみ変更",
103
+ "カスタムサイズ",
104
+ ],
105
+ "custom_size_eror": "幅は長さより大きくしないでください。長さと幅は100以上1800以下にしてください。",
106
+ },
107
+ "ko": {
108
+ "label": "증명사진 크기 옵션",
109
+ "choices": ["크기 목록", "배경만 변경", "사용자 지정 크기"],
110
+ "custom_size_eror": "너비는 길이보다 크지 않아야 합니다; 길이와 너비는 100 이상 1800 이하여야 합니다.",
111
+ },
112
  },
113
  "size_list": {
114
  "en": {
 
121
  "choices": list(size_list_dict_CN.keys()),
122
  "develop": size_list_config_CN,
123
  },
124
+ "ja": {
125
+ "label": "サイズリスト",
126
+ "choices": list(size_list_dict_EN.keys()),
127
+ "develop": size_list_config_EN,
128
+ },
129
+ "ko": {
130
+ "label": "크기 목록",
131
+ "choices": list(size_list_dict_EN.keys()),
132
+ "develop": size_list_config_EN,
133
+ },
134
  },
135
  "bg_color": {
136
  "en": {
 
143
  "choices": list(color_list_dict_CN.keys()) + ["自定义底色"],
144
  "develop": color_list_dict_CN,
145
  },
146
+ "ja": {
147
+ "label": "背景色",
148
+ "choices": list(color_list_dict_EN.keys()) + ["カスタム"],
149
+ "develop": color_list_dict_EN,
150
+ },
151
+ "ko": {
152
+ "label": "배경색",
153
+ "choices": list(color_list_dict_EN.keys()) + ["사용자 지정"],
154
+ "develop": color_list_dict_EN,
155
+ },
156
  },
157
  "button": {
158
  "en": {
 
161
  "zh": {
162
  "label": "开始制作",
163
  },
164
+ "ja": {
165
+ "label": "開始",
166
+ },
167
+ "ko": {
168
+ "label": "시작",
169
+ },
170
  },
171
  "head_measure_ratio": {
172
  "en": {
 
175
  "zh": {
176
  "label": "面部比例",
177
  },
178
+ "ja": {
179
+ "label": "頭部比率",
180
+ },
181
+ "ko": {
182
+ "label": "머리 비율",
183
+ },
184
  },
185
  "top_distance": {
186
  "en": {
 
189
  "zh": {
190
  "label": "头距顶距离",
191
  },
192
+ "ja": {
193
+ "label": "上部からの距離",
194
+ },
195
+ "ko": {
196
+ "label": "상단 거리",
197
+ },
198
  },
199
  "image_kb": {
200
  "en": {
 
205
  "label": "设置 KB 大小",
206
  "choices": ["不设置", "自定义"],
207
  },
208
+ "ja": {
209
+ "label": "KBサイズを設定",
210
+ "choices": ["設定なし", "カスタム"],
211
+ },
212
+ "ko": {
213
+ "label": "KB 크기 설정",
214
+ "choices": ["설정 안 함", "사용자 지정"],
215
+ },
216
  },
217
  "image_kb_size": {
218
  "en": {
 
221
  "zh": {
222
  "label": "KB 大小",
223
  },
224
+ "ja": {
225
+ "label": "KBサイズ",
226
+ },
227
+ "ko": {
228
+ "label": "KB 크기",
229
+ },
230
+ },
231
+ "image_dpi": {
232
+ "en": {
233
+ "label": "Set DPI",
234
+ "choices": ["Not Set", "Custom"],
235
+ },
236
+ "zh": {
237
+ "label": "设置 DPI 大小",
238
+ "choices": ["不设置", "自定义"],
239
+ },
240
+ "ja": {
241
+ "label": "DPIを設定",
242
+ "choices": ["設定なし", "カスタム"],
243
+ },
244
+ "ko": {
245
+ "label": "DPI 설정",
246
+ "choices": ["설정 안 함", "사용자 지정"],
247
+ },
248
+ },
249
+ "image_dpi_size": {
250
+ "en": {
251
+ "label": "DPI size",
252
+ },
253
+ "zh": {
254
+ "label": "DPI 大小",
255
+ },
256
+ "ja": {
257
+ "label": "DPIサイズ",
258
+ },
259
+ "ko": {
260
+ "label": "DPI 크기",
261
+ },
262
  },
263
  "render_mode": {
264
  "en": {
 
273
  "label": "渲染方式",
274
  "choices": ["纯色", "上下渐变(白色)", "中心渐变(白色)"],
275
  },
276
+ "ja": {
277
+ "label": "レンダリングモード",
278
+ "choices": [
279
+ "単色",
280
+ "上下グラデーション(白)",
281
+ "中心グラデーション(白)",
282
+ ],
283
+ },
284
+ "ko": {
285
+ "label": "렌더링 모드",
286
+ "choices": [
287
+ "단색",
288
+ "위-아래 그라데이션 (흰색)",
289
+ "중앙 그라데이션 (흰색)",
290
+ ],
291
+ },
292
  },
293
  # Tab3 - 水印工作台
294
  "watermark_tab": {
 
298
  "zh": {
299
  "label": "水印",
300
  },
301
+ "ja": {
302
+ "label": "ウォーターマーク",
303
+ },
304
+ "ko": {
305
+ "label": "워터마크",
306
+ },
307
  },
308
  "watermark_text": {
309
  "en": {
 
316
  "value": "Hello",
317
  "placeholder": "最多20个字符",
318
  },
319
+ "ja": {
320
+ "label": "テキスト",
321
+ "value": "Hello",
322
+ "placeholder": "最大20文字",
323
+ },
324
+ "ko": {
325
+ "label": "텍스트",
326
+ "value": "Hello",
327
+ "placeholder": "최대 20자",
328
+ },
329
  },
330
  "watermark_color": {
331
  "en": {
 
334
  "zh": {
335
  "label": "水印颜色",
336
  },
337
+ "ja": {
338
+ "label": "色",
339
+ },
340
+ "ko": {
341
+ "label": "색상",
342
+ },
343
  },
344
  "watermark_size": {
345
  "en": {
 
348
  "zh": {
349
  "label": "文字大小",
350
  },
351
+ "ja": {
352
+ "label": "サイズ",
353
+ },
354
+ "ko": {
355
+ "label": "크기",
356
+ },
357
  },
358
  "watermark_opacity": {
359
  "en": {
 
362
  "zh": {
363
  "label": "水印透明度",
364
  },
365
+ "ja": {
366
+ "label": "不透明度",
367
+ },
368
+ "ko": {
369
+ "label": "불투명도",
370
+ },
371
  },
372
  "watermark_angle": {
373
  "en": {
 
376
  "zh": {
377
  "label": "水印角度",
378
  },
379
+ "ja": {
380
+ "label": "角度",
381
+ },
382
+ "ko": {
383
+ "label": "각도",
384
+ },
385
  },
386
  "watermark_space": {
387
  "en": {
 
390
  "zh": {
391
  "label": "水印间距",
392
  },
393
+ "ja": {
394
+ "label": "間隔",
395
+ },
396
+ "ko": {
397
+ "label": "간격",
398
+ },
399
  },
400
  "watermark_switch": {
401
  "en": {
 
408
  "value": "不添加",
409
  "choices": ["不添加", "添加"],
410
  },
411
+ "ja": {
412
+ "label": "ウォーターマーク",
413
+ "value": "追加しない",
414
+ "choices": ["追加しない", "追加"],
415
+ },
416
+ "ko": {
417
+ "label": "워터마크",
418
+ "value": "추가하지 않음",
419
+ "choices": ["추가하지 않음", "추가"],
420
+ },
421
  },
422
  # 输出结果
423
  "notification": {
 
429
  "label": "通知",
430
  "face_error": "人脸数不等于1,请上传单人照片。如果实际人脸数为1,可能是检测模型的准确度问题,请切换左侧不同的人脸检测模型或提出Github Issue通知作者。",
431
  },
432
+ "ja": {
433
+ "label": "通知",
434
+ "face_error": "顔の数が1ではありません。1つの顔を含む画像をアップロードしてください。実際の顔の数が1の場合、検出モデルの精度の問題かもしれません。左側で別の顔検出モデルに切り替えるか、Githubの問題を作成して作者に通知してください。",
435
+ },
436
+ "ko": {
437
+ "label": "알림",
438
+ "face_error": "얼굴 수가 1이 아닙니다. 단일 얼굴이 있는 이미지를 업로드해 주세요. 실제 얼굴 수가 1인 경우 감지 모델의 정확도 문제일 수 있습니다. 왼쪽에서 다른 얼굴 감지 모델로 전환하거나 Github Issue를 제기하여 작성자에게 알려주세요.",
439
+ },
440
  },
441
  "standard_photo": {
442
  "en": {
 
445
  "zh": {
446
  "label": "标准照",
447
  },
448
+ "ja": {
449
+ "label": "標準写真",
450
+ },
451
+ "ko": {
452
+ "label": "표준 사진",
453
+ },
454
  },
455
  "hd_photo": {
456
  "en": {
 
459
  "zh": {
460
  "label": "高清照",
461
  },
462
+ "ja": {
463
+ "label": "HD写真",
464
+ },
465
+ "ko": {
466
+ "label": "HD 사진",
467
+ },
468
  },
469
  "standard_photo_png": {
470
  "en": {
 
473
  "zh": {
474
  "label": "透明标准照",
475
  },
476
+ "ja": {
477
+ "label": "マッティング標準写真",
478
+ },
479
+ "ko": {
480
+ "label": "매팅 표준 사진",
481
+ },
482
  },
483
  "hd_photo_png": {
484
  "en": {
 
487
  "zh": {
488
  "label": "透明高清照",
489
  },
490
+ "ja": {
491
+ "label": "マッティングHD写真",
492
+ },
493
+ "ko": {
494
+ "label": "매팅 HD 사진",
495
+ },
496
  },
497
  "layout_photo": {
498
  "en": {
 
501
  "zh": {
502
  "label": "六寸排版照",
503
  },
504
+ "ja": {
505
+ "label": "レイアウト写真",
506
+ },
507
+ "ko": {
508
+ "label": "레이아웃 사진",
509
+ },
510
  },
511
  "download": {
512
  "en": {
513
+ "label": "Download the photo after adjusting the DPI or KB size",
514
  },
515
  "zh": {
516
+ "label": "下载调整 DPI 或 KB 大小后的照片",
517
+ },
518
+ "ja": {
519
+ "label": "DPIまたはKBサイズ調整後の写真をダウンロード",
520
+ },
521
+ "ko": {
522
+ "label": "DPI 또는 KB 크기 조정 후 사진 다운로드",
523
  },
524
  },
525
  "matting_image": {
 
529
  "zh": {
530
  "label": "抠图图像",
531
  },
532
+ "ja": {
533
+ "label": "マット画像",
534
+ },
535
+ "ko": {
536
+ "label": "매팅 이미지",
537
+ },
538
  },
539
  "beauty_tab": {
540
  "en": {
 
543
  "zh": {
544
  "label": "美颜",
545
  },
546
+ "ja": {
547
+ "label": "美顔",
548
+ },
549
+ "ko": {
550
+ "label": "뷰티",
551
+ },
552
  },
553
  "whitening_strength": {
554
  "en": {
 
557
  "zh": {
558
  "label": "美白强度",
559
  },
560
+ "ja": {
561
+ "label": "美白強度",
562
+ },
563
+ "ko": {
564
+ "label": "미백 강도",
565
+ },
566
+ },
567
+ "brightness_strength": {
568
+ "en": {
569
+ "label": "brightness strength",
570
+ },
571
+ "zh": {
572
+ "label": "亮度强度",
573
+ },
574
+ "ja": {
575
+ "label": "明るさの強さ",
576
+ },
577
+ "ko": {
578
+ "label": "밝기 강도",
579
+ },
580
+ },
581
+ "contrast_strength": {
582
+ "en": {
583
+ "label": "contrast strength",
584
+ },
585
+ "zh": {
586
+ "label": "对比度强度",
587
+ },
588
+ "ja": {
589
+ "label": "コントラスト強度",
590
+ },
591
+ "ko": {
592
+ "label": "대비 강도",
593
+ },
594
+ },
595
+ "sharpen_strength": {
596
+ "en": {
597
+ "label": "sharpen strength",
598
+ },
599
+ "zh": {
600
+ "label": "锐化强度",
601
+ },
602
+ "ja": {
603
+ "label": "シャープ化強度",
604
+ },
605
+ "ko": {
606
+ "label": "샤ープ 강도",
607
+ },
608
+ },
609
+ "saturation_strength": {
610
+ "en": {
611
+ "label": "saturation strength",
612
+ },
613
+ "zh": {
614
+ "label": "饱和度强度",
615
+ },
616
+ "ja": {
617
+ "label": "飽和度強度",
618
+ },
619
+ "ko": {
620
+ "label": "포화도 강도",
621
+ },
622
  },
623
  }
demo/processor.py CHANGED
@@ -1,7 +1,12 @@
1
  import numpy as np
2
  from hivision import IDCreator
3
  from hivision.error import FaceError, APIError
4
- from hivision.utils import add_background, resize_image_to_kb, add_watermark
 
 
 
 
 
5
  from hivision.creator.layout_calculator import (
6
  generate_layout_photo,
7
  generate_layout_image,
@@ -42,265 +47,406 @@ class IDPhotoProcessor:
42
  head_measure_ratio=0.2,
43
  top_distance_max=0.12,
44
  whitening_strength=0,
 
 
 
 
 
 
45
  ):
 
46
  top_distance_min = top_distance_max - 0.02
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
- idphoto_json = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  "size_mode": mode_option,
50
  "color_mode": color_option,
51
  "render_mode": render_option,
52
  "image_kb_mode": image_kb_options,
53
  "custom_image_kb": None,
 
54
  }
55
 
56
- # 如果尺寸模式选择的是尺寸列表
 
 
 
 
 
 
 
 
57
  if idphoto_json["size_mode"] == LOCALES["size_mode"][language]["choices"][0]:
58
  idphoto_json["size"] = LOCALES["size_list"][language]["develop"][
59
  size_list_option
60
  ]
61
- # 如果尺寸模式选择的是自定义尺寸
62
  elif idphoto_json["size_mode"] == LOCALES["size_mode"][language]["choices"][2]:
63
- id_height = int(custom_size_height)
64
- id_width = int(custom_size_width)
65
  if (
66
  id_height < id_width
67
  or min(id_height, id_width) < 100
68
  or max(id_height, id_width) > 1800
69
  ):
70
- return [
71
- gr.update(value=None), # img_output_standard
72
- gr.update(value=None), # img_output_standard_hd
73
- gr.update(value=None), # img_output_standard_png
74
- gr.update(value=None), # img_output_standard_hd_png
75
- None, # img_output_layout (assuming it should be None or not updated)
76
- gr.update( # notification
77
- value=LOCALES["size_mode"][language]["custom_size_eror"],
78
- visible=True,
79
- ),
80
- None, # file_download (assuming it should be None or not updated)
81
- ]
82
-
83
  idphoto_json["size"] = (id_height, id_width)
84
  else:
85
  idphoto_json["size"] = (None, None)
86
 
87
- # 如果颜色模式选择的是自定义底色
 
 
 
 
 
 
 
 
 
88
  if idphoto_json["color_mode"] == LOCALES["bg_color"][language]["choices"][-1]:
89
- idphoto_json["color_bgr"] = (
90
- range_check(custom_color_R),
91
- range_check(custom_color_G),
92
- range_check(custom_color_B),
93
  )
94
  else:
95
- hex_color = idphoto_json["color_bgr"] = LOCALES["bg_color"][language][
96
- "develop"
97
- ][color_option]
98
- # 转为 RGB
99
  idphoto_json["color_bgr"] = tuple(
100
  int(hex_color[i : i + 2], 16) for i in (0, 2, 4)
101
  )
102
 
103
- # 如果输出 KB 大小选择的是自定义
104
- if (
105
- idphoto_json["image_kb_mode"]
106
- == LOCALES["image_kb"][language]["choices"][-1]
107
- ):
108
- idphoto_json["custom_image_kb"] = custom_image_kb
109
-
110
- creator = IDCreator()
111
- choose_handler(creator, matting_model_option, face_detect_option)
112
-
113
- # 是否只换底
 
 
 
 
 
114
  change_bg_only = (
115
  idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]
116
  )
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
- # 生成证件照
119
- try:
120
- result = creator(
121
- input_image,
122
- change_bg_only=change_bg_only,
123
- size=idphoto_json["size"],
124
- head_measure_ratio=head_measure_ratio,
125
- head_top_range=(top_distance_max, top_distance_min),
126
- whitening_strength=whitening_strength,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  )
128
- # 如果检测到人脸数量不等于1
129
- except FaceError:
130
- return [
131
- gr.update(value=None), # img_output_standard
132
- gr.update(value=None), # img_output_standard_hd
133
- gr.update(value=None), # img_output_standard_png
134
- gr.update(value=None), # img_output_standard_hd_png
135
- gr.update(visible=False), # img_output_layout
136
- gr.update( # notification
137
- value=LOCALES["notification"][language]["face_error"],
138
- visible=True,
139
- ),
140
- None, # file_download (assuming it should be None or have no update)
141
- ]
142
- # 如果 API 错误
143
- except APIError as e:
144
- return [
145
- gr.update(value=None), # img_output_standard
146
- gr.update(value=None), # img_output_standard_hd
147
- gr.update(value=None), # img_output_standard_png
148
- gr.update(value=None), # img_output_standard_hd_png
149
- gr.update(visible=False), # img_output_layout
150
- gr.update( # notification
151
- value=LOCALES["notification"][language]["face_error"],
152
- visible=True,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  ),
154
- None, # file_download (assuming it should be None or have no update)
155
- ]
156
- # 证件照生成正常
157
- else:
158
- (result_image_standard, result_image_hd, _, _, _, _) = result
 
 
 
 
 
159
 
160
- result_image_standard_png = np.uint8(result_image_standard)
161
- result_image_hd_png = np.uint8(result_image_hd)
162
 
163
- # 纯色渲染
164
- if (
165
- idphoto_json["render_mode"]
166
- == LOCALES["render_mode"][language]["choices"][0]
167
- ):
168
- result_image_standard = np.uint8(
169
- add_background(result_image_standard, bgr=idphoto_json["color_bgr"])
170
- )
171
- result_image_hd = np.uint8(
172
- add_background(result_image_hd, bgr=idphoto_json["color_bgr"])
173
- )
174
- # 上下渐变渲染
175
- elif (
176
- idphoto_json["render_mode"]
177
- == LOCALES["render_mode"][language]["choices"][1]
178
- ):
179
- result_image_standard = np.uint8(
180
- add_background(
181
- result_image_standard,
182
- bgr=idphoto_json["color_bgr"],
183
- mode="updown_gradient",
184
- )
185
- )
186
- result_image_hd = np.uint8(
187
- add_background(
188
- result_image_hd,
189
- bgr=idphoto_json["color_bgr"],
190
- mode="updown_gradient",
191
- )
192
- )
193
- # 中心渐变渲染
194
- else:
195
- result_image_standard = np.uint8(
196
- add_background(
197
- result_image_standard,
198
- bgr=idphoto_json["color_bgr"],
199
- mode="center_gradient",
200
- )
201
- )
202
- result_image_hd = np.uint8(
203
- add_background(
204
- result_image_hd,
205
- bgr=idphoto_json["color_bgr"],
206
- mode="center_gradient",
207
- )
208
- )
209
-
210
- # 如果只换底,就不生成排版照
211
- if change_bg_only:
212
- result_layout_image = gr.update(visible=False)
213
- else:
214
- typography_arr, typography_rotate = generate_layout_photo(
215
- input_height=idphoto_json["size"][0],
216
- input_width=idphoto_json["size"][1],
217
- )
218
-
219
- if (
220
- watermark_option
221
- == LOCALES["watermark_switch"][language]["choices"][1]
222
- ):
223
- result_layout_image = gr.update(
224
- value=generate_layout_image(
225
- add_watermark(
226
- image=result_image_standard,
227
- text=watermark_text,
228
- size=watermark_text_size,
229
- opacity=watermark_text_opacity,
230
- angle=watermark_text_angle,
231
- space=watermark_text_space,
232
- color=watermark_text_color,
233
- ),
234
- typography_arr,
235
- typography_rotate,
236
- height=idphoto_json["size"][0],
237
- width=idphoto_json["size"][1],
238
- ),
239
- visible=True,
240
- )
241
- else:
242
- result_layout_image = gr.update(
243
- value=generate_layout_image(
244
- result_image_standard,
245
- typography_arr,
246
- typography_rotate,
247
- height=idphoto_json["size"][0],
248
- width=idphoto_json["size"][1],
249
- ),
250
- visible=True,
251
- )
252
-
253
- # 如果添加水印
254
- if watermark_option == LOCALES["watermark_switch"][language]["choices"][1]:
255
- result_image_standard = add_watermark(
256
- image=result_image_standard,
257
- text=watermark_text,
258
- size=watermark_text_size,
259
- opacity=watermark_text_opacity,
260
- angle=watermark_text_angle,
261
- space=watermark_text_space,
262
- color=watermark_text_color,
263
- )
264
- result_image_hd = add_watermark(
265
- image=result_image_hd,
266
- text=watermark_text,
267
- size=watermark_text_size,
268
- opacity=watermark_text_opacity,
269
- angle=watermark_text_angle,
270
- space=watermark_text_space,
271
- color=watermark_text_color,
272
- )
273
-
274
- # 如果输出 KB 大小选择的是自定义
275
- if idphoto_json["custom_image_kb"]:
276
- print("调整 kb 大小到", idphoto_json["custom_image_kb"], "kb")
277
- output_image_path = f"{os.path.join(os.path.dirname(os.path.dirname(__file__)), 'demo/kb_output')}/{int(time.time())}.jpg"
278
- resize_image_to_kb(
279
- result_image_standard,
280
- output_image_path,
281
- idphoto_json["custom_image_kb"],
282
- )
283
- else:
284
- output_image_path = None
285
-
286
- # 返回结果
287
- if output_image_path:
288
- return [
289
- result_image_standard, # img_output_standard
290
- result_image_hd, # img_output_standard_hd
291
- result_image_standard_png, # img_output_standard_png
292
- result_image_hd_png, # img_output_standard_hd_png
293
- result_layout_image, # img_output_layout
294
- gr.update(visible=False), # notification
295
- gr.update(visible=True, value=output_image_path), # file_download
296
- ]
297
- else:
298
- return [
299
- result_image_standard, # img_output_standard
300
- result_image_hd, # img_output_standard_hd
301
- result_image_standard_png, # img_output_standard_png
302
- result_image_hd_png, # img_output_standard_hd_png
303
- result_layout_image, # img_output_layout
304
- gr.update(visible=False), # notification
305
- gr.update(visible=False), # file_download
306
- ]
 
1
  import numpy as np
2
  from hivision import IDCreator
3
  from hivision.error import FaceError, APIError
4
+ from hivision.utils import (
5
+ add_background,
6
+ resize_image_to_kb,
7
+ add_watermark,
8
+ save_image_dpi_to_bytes,
9
+ )
10
  from hivision.creator.layout_calculator import (
11
  generate_layout_photo,
12
  generate_layout_image,
 
47
  head_measure_ratio=0.2,
48
  top_distance_max=0.12,
49
  whitening_strength=0,
50
+ image_dpi_option=False,
51
+ custom_image_dpi=None,
52
+ brightness_strength=0,
53
+ contrast_strength=0,
54
+ sharpen_strength=0,
55
+ saturation_strength=0,
56
  ):
57
+ # 初始化参数
58
  top_distance_min = top_distance_max - 0.02
59
+ # 得到render_option在LOCALES["render_mode"][language]["choices"]中的索引
60
+ render_option_index = LOCALES["render_mode"][language]["choices"].index(
61
+ render_option
62
+ )
63
+ idphoto_json = self._initialize_idphoto_json(
64
+ mode_option, color_option, render_option_index, image_kb_options
65
+ )
66
+
67
+ # 处理尺寸模式
68
+ size_result = self._process_size_mode(
69
+ idphoto_json,
70
+ language,
71
+ size_list_option,
72
+ custom_size_height,
73
+ custom_size_width,
74
+ )
75
+ if isinstance(size_result, list):
76
+ return size_result # 返回错误信息
77
+
78
+ # 处理颜色模式
79
+ self._process_color_mode(
80
+ idphoto_json,
81
+ language,
82
+ color_option,
83
+ custom_color_R,
84
+ custom_color_G,
85
+ custom_color_B,
86
+ )
87
+
88
+ # 如果设置了自定义KB大小
89
+ if (
90
+ idphoto_json["image_kb_mode"]
91
+ == LOCALES["image_kb"][language]["choices"][-1]
92
+ ):
93
+ idphoto_json["custom_image_kb"] = custom_image_kb
94
+
95
+ # 如果设置了自定义DPI大小
96
+ if image_dpi_option == LOCALES["image_dpi"][language]["choices"][-1]:
97
+ idphoto_json["custom_image_dpi"] = custom_image_dpi
98
+
99
+ # 创建IDCreator实例并设置处理器
100
+ creator = IDCreator()
101
+ choose_handler(creator, matting_model_option, face_detect_option)
102
 
103
+ # 生成证件照
104
+ try:
105
+ result = self._generate_id_photo(
106
+ creator,
107
+ input_image,
108
+ idphoto_json,
109
+ language,
110
+ head_measure_ratio,
111
+ top_distance_max,
112
+ top_distance_min,
113
+ whitening_strength,
114
+ brightness_strength,
115
+ contrast_strength,
116
+ sharpen_strength,
117
+ saturation_strength,
118
+ )
119
+ except (FaceError, APIError):
120
+ return self._handle_photo_generation_error(language)
121
+
122
+ # 后处理生成的照片
123
+ return self._process_generated_photo(
124
+ result,
125
+ idphoto_json,
126
+ language,
127
+ watermark_option,
128
+ watermark_text,
129
+ watermark_text_size,
130
+ watermark_text_opacity,
131
+ watermark_text_angle,
132
+ watermark_text_space,
133
+ watermark_text_color,
134
+ )
135
+
136
+ def _initialize_idphoto_json(
137
+ self,
138
+ mode_option,
139
+ color_option,
140
+ render_option,
141
+ image_kb_options,
142
+ ):
143
+ """初始化idphoto_json字典"""
144
+ return {
145
  "size_mode": mode_option,
146
  "color_mode": color_option,
147
  "render_mode": render_option,
148
  "image_kb_mode": image_kb_options,
149
  "custom_image_kb": None,
150
+ "custom_image_dpi": None,
151
  }
152
 
153
+ def _process_size_mode(
154
+ self,
155
+ idphoto_json,
156
+ language,
157
+ size_list_option,
158
+ custom_size_height,
159
+ custom_size_width,
160
+ ):
161
+ """处理尺寸模式"""
162
  if idphoto_json["size_mode"] == LOCALES["size_mode"][language]["choices"][0]:
163
  idphoto_json["size"] = LOCALES["size_list"][language]["develop"][
164
  size_list_option
165
  ]
 
166
  elif idphoto_json["size_mode"] == LOCALES["size_mode"][language]["choices"][2]:
167
+ id_height, id_width = int(custom_size_height), int(custom_size_width)
 
168
  if (
169
  id_height < id_width
170
  or min(id_height, id_width) < 100
171
  or max(id_height, id_width) > 1800
172
  ):
173
+ return self._create_error_response(language)
 
 
 
 
 
 
 
 
 
 
 
 
174
  idphoto_json["size"] = (id_height, id_width)
175
  else:
176
  idphoto_json["size"] = (None, None)
177
 
178
+ def _process_color_mode(
179
+ self,
180
+ idphoto_json,
181
+ language,
182
+ color_option,
183
+ custom_color_R,
184
+ custom_color_G,
185
+ custom_color_B,
186
+ ):
187
+ """处理颜色模式"""
188
  if idphoto_json["color_mode"] == LOCALES["bg_color"][language]["choices"][-1]:
189
+ idphoto_json["color_bgr"] = tuple(
190
+ map(range_check, [custom_color_R, custom_color_G, custom_color_B])
 
 
191
  )
192
  else:
193
+ hex_color = LOCALES["bg_color"][language]["develop"][color_option]
 
 
 
194
  idphoto_json["color_bgr"] = tuple(
195
  int(hex_color[i : i + 2], 16) for i in (0, 2, 4)
196
  )
197
 
198
+ def _generate_id_photo(
199
+ self,
200
+ creator: IDCreator,
201
+ input_image,
202
+ idphoto_json,
203
+ language,
204
+ head_measure_ratio,
205
+ top_distance_max,
206
+ top_distance_min,
207
+ whitening_strength,
208
+ brightness_strength,
209
+ contrast_strength,
210
+ sharpen_strength,
211
+ saturation_strength,
212
+ ):
213
+ """生成证件照"""
214
  change_bg_only = (
215
  idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]
216
  )
217
+ return creator(
218
+ input_image,
219
+ change_bg_only=change_bg_only,
220
+ size=idphoto_json["size"],
221
+ head_measure_ratio=head_measure_ratio,
222
+ head_top_range=(top_distance_max, top_distance_min),
223
+ whitening_strength=whitening_strength,
224
+ brightness_strength=brightness_strength,
225
+ contrast_strength=contrast_strength,
226
+ sharpen_strength=sharpen_strength,
227
+ saturation_strength=saturation_strength,
228
+ )
229
 
230
+ def _handle_photo_generation_error(self, language):
231
+ """处理照片生成错误"""
232
+ return [gr.update(value=None) for _ in range(4)] + [
233
+ gr.update(visible=False),
234
+ gr.update(
235
+ value=LOCALES["notification"][language]["face_error"], visible=True
236
+ ),
237
+ None,
238
+ ]
239
+
240
+ def _process_generated_photo(
241
+ self,
242
+ result,
243
+ idphoto_json,
244
+ language,
245
+ watermark_option,
246
+ watermark_text,
247
+ watermark_text_size,
248
+ watermark_text_opacity,
249
+ watermark_text_angle,
250
+ watermark_text_space,
251
+ watermark_text_color,
252
+ ):
253
+ """处理生成的照片"""
254
+ result_image_standard, result_image_hd, _, _, _, _ = result
255
+ result_image_standard_png = np.uint8(result_image_standard)
256
+ result_image_hd_png = np.uint8(result_image_hd)
257
+
258
+ # 渲染背景
259
+ result_image_standard, result_image_hd = self._render_background(
260
+ result_image_standard, result_image_hd, idphoto_json
261
+ )
262
+
263
+ # 生成排版照片
264
+ result_layout_image = self._generate_layout_image(
265
+ idphoto_json,
266
+ result_image_standard,
267
+ language,
268
+ watermark_option,
269
+ watermark_text,
270
+ watermark_text_size,
271
+ watermark_text_opacity,
272
+ watermark_text_angle,
273
+ watermark_text_space,
274
+ watermark_text_color,
275
+ )
276
+
277
+ # 添加水印
278
+ if watermark_option == LOCALES["watermark_switch"][language]["choices"][1]:
279
+ result_image_standard, result_image_hd = self._add_watermark(
280
+ result_image_standard,
281
+ result_image_hd,
282
+ watermark_text,
283
+ watermark_text_size,
284
+ watermark_text_opacity,
285
+ watermark_text_angle,
286
+ watermark_text_space,
287
+ watermark_text_color,
288
  )
289
+
290
+ # 调整图片大小
291
+ output_image_path = self._resize_image_if_needed(
292
+ result_image_standard, idphoto_json
293
+ )
294
+
295
+ return self._create_response(
296
+ result_image_standard,
297
+ result_image_hd,
298
+ result_image_standard_png,
299
+ result_image_hd_png,
300
+ result_layout_image,
301
+ output_image_path,
302
+ )
303
+
304
+ def _render_background(self, result_image_standard, result_image_hd, idphoto_json):
305
+ """渲染背景"""
306
+ render_modes = {0: "pure_color", 1: "updown_gradient", 2: "center_gradient"}
307
+ render_mode = render_modes[idphoto_json["render_mode"]]
308
+
309
+ result_image_standard = np.uint8(
310
+ add_background(
311
+ result_image_standard, bgr=idphoto_json["color_bgr"], mode=render_mode
312
+ )
313
+ )
314
+ result_image_hd = np.uint8(
315
+ add_background(
316
+ result_image_hd, bgr=idphoto_json["color_bgr"], mode=render_mode
317
+ )
318
+ )
319
+
320
+ return result_image_standard, result_image_hd
321
+
322
+ def _generate_layout_image(
323
+ self,
324
+ idphoto_json,
325
+ result_image_standard,
326
+ language,
327
+ watermark_option,
328
+ watermark_text,
329
+ watermark_text_size,
330
+ watermark_text_opacity,
331
+ watermark_text_angle,
332
+ watermark_text_space,
333
+ watermark_text_color,
334
+ ):
335
+ """生成排版照片"""
336
+ if idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]:
337
+ return gr.update(visible=False)
338
+
339
+ typography_arr, typography_rotate = generate_layout_photo(
340
+ input_height=idphoto_json["size"][0],
341
+ input_width=idphoto_json["size"][1],
342
+ )
343
+
344
+ image = result_image_standard
345
+ if watermark_option == LOCALES["watermark_switch"][language]["choices"][1]:
346
+ image = add_watermark(
347
+ image=image,
348
+ text=watermark_text,
349
+ size=watermark_text_size,
350
+ opacity=watermark_text_opacity,
351
+ angle=watermark_text_angle,
352
+ space=watermark_text_space,
353
+ color=watermark_text_color,
354
+ )
355
+
356
+ return gr.update(
357
+ value=generate_layout_image(
358
+ image,
359
+ typography_arr,
360
+ typography_rotate,
361
+ height=idphoto_json["size"][0],
362
+ width=idphoto_json["size"][1],
363
+ ),
364
+ visible=True,
365
+ )
366
+
367
+ def _add_watermark(
368
+ self,
369
+ result_image_standard,
370
+ result_image_hd,
371
+ watermark_text,
372
+ watermark_text_size,
373
+ watermark_text_opacity,
374
+ watermark_text_angle,
375
+ watermark_text_space,
376
+ watermark_text_color,
377
+ ):
378
+ """添加水印"""
379
+ watermark_params = {
380
+ "text": watermark_text,
381
+ "size": watermark_text_size,
382
+ "opacity": watermark_text_opacity,
383
+ "angle": watermark_text_angle,
384
+ "space": watermark_text_space,
385
+ "color": watermark_text_color,
386
+ }
387
+ result_image_standard = add_watermark(
388
+ image=result_image_standard, **watermark_params
389
+ )
390
+ result_image_hd = add_watermark(image=result_image_hd, **watermark_params)
391
+ return result_image_standard, result_image_hd
392
+
393
+ def _resize_image_if_needed(self, result_image_standard, idphoto_json):
394
+ """如果需要,调整图片大小"""
395
+ output_image_path = f"{os.path.join(os.path.dirname(os.path.dirname(__file__)), 'demo/kb_output')}/{int(time.time())}.jpg"
396
+ # 如果设置了自定义KB大小
397
+ if idphoto_json["custom_image_kb"]:
398
+ resize_image_to_kb(
399
+ result_image_standard,
400
+ output_image_path,
401
+ idphoto_json["custom_image_kb"],
402
+ dpi=(
403
+ idphoto_json["custom_image_dpi"]
404
+ if idphoto_json["custom_image_dpi"]
405
+ else 300
406
  ),
407
+ )
408
+ return output_image_path
409
+ # 如果只设置了dpi
410
+ elif idphoto_json["custom_image_dpi"]:
411
+ save_image_dpi_to_bytes(
412
+ result_image_standard,
413
+ output_image_path,
414
+ dpi=idphoto_json["custom_image_dpi"],
415
+ )
416
+ return output_image_path
417
 
418
+ return None
 
419
 
420
+ def _create_response(
421
+ self,
422
+ result_image_standard,
423
+ result_image_hd,
424
+ result_image_standard_png,
425
+ result_image_hd_png,
426
+ result_layout_image,
427
+ output_image_path,
428
+ ):
429
+ """创建响应"""
430
+ response = [
431
+ result_image_standard,
432
+ result_image_hd,
433
+ result_image_standard_png,
434
+ result_image_hd_png,
435
+ result_layout_image,
436
+ gr.update(visible=False),
437
+ ]
438
+ if output_image_path:
439
+ response.append(gr.update(visible=True, value=output_image_path))
440
+ else:
441
+ response.append(gr.update(visible=False))
442
+ return response
443
+
444
+ def _create_error_response(self, language):
445
+ """创建错误响应"""
446
+ return [gr.update(value=None) for _ in range(4)] + [
447
+ None,
448
+ gr.update(
449
+ value=LOCALES["size_mode"][language]["custom_size_eror"], visible=True
450
+ ),
451
+ None,
452
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
demo/ui.py CHANGED
@@ -21,8 +21,9 @@ def create_ui(
21
  root_dir: str,
22
  human_matting_models: list,
23
  face_detect_models: list,
 
24
  ):
25
- DEFAULT_LANG = "zh"
26
  DEFAULT_HUMAN_MATTING_MODEL = "modnet_photographic_portrait_matting"
27
  DEFAULT_FACE_DETECT_MODEL = "retinaface-resnet50"
28
 
@@ -38,13 +39,12 @@ def create_ui(
38
  with demo:
39
  gr.HTML(load_description(os.path.join(root_dir, "assets/title.md")))
40
  with gr.Row():
41
- # ------------ 左半边 UI ----------------
42
  with gr.Column():
43
  img_input = gr.Image(height=400)
44
 
45
  with gr.Row():
46
  # 语言选择器
47
- language = ["zh", "en"]
48
  language_options = gr.Dropdown(
49
  choices=language,
50
  label="Language",
@@ -63,7 +63,7 @@ def create_ui(
63
  value=human_matting_models[0],
64
  )
65
 
66
- # TAB1 - 关键参数
67
  with gr.Tab(
68
  LOCALES["key_param"][DEFAULT_LANG]["label"]
69
  ) as key_parameter_tab:
@@ -106,7 +106,7 @@ def create_ui(
106
  value=LOCALES["render_mode"][DEFAULT_LANG]["choices"][0],
107
  )
108
 
109
- # TAB2 - 高级参数
110
  with gr.Tab(
111
  LOCALES["advance_param"][DEFAULT_LANG]["label"]
112
  ) as advance_parameter_tab:
@@ -133,19 +133,34 @@ def create_ui(
133
  value=LOCALES["image_kb"][DEFAULT_LANG]["choices"][0],
134
  )
135
 
136
- with gr.Row(visible=False) as custom_image_kb:
137
- custom_image_kb_size = gr.Slider(
138
- minimum=10,
139
- maximum=1000,
140
- value=50,
141
- label=LOCALES["image_kb_size"][DEFAULT_LANG]["label"],
142
- interactive=True,
143
- )
144
 
145
- # TAB3 - 美颜
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  with gr.Tab(
147
  LOCALES["beauty_tab"][DEFAULT_LANG]["label"]
148
  ) as beauty_parameter_tab:
 
149
  whitening_option = gr.Slider(
150
  label=LOCALES["whitening_strength"][DEFAULT_LANG]["label"],
151
  minimum=0,
@@ -155,7 +170,46 @@ def create_ui(
155
  interactive=True,
156
  )
157
 
158
- # TAB4 - 水印
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  with gr.Tab(
160
  LOCALES["watermark_tab"][DEFAULT_LANG]["label"]
161
  ) as watermark_parameter_tab:
@@ -409,27 +463,46 @@ def create_ui(
409
  whitening_option: gr.update(
410
  label=LOCALES["whitening_strength"][language]["label"]
411
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  }
413
 
414
- def change_color(colors):
415
- if colors == "自定义底色" or colors == "Custom Color":
416
- return {custom_color: gr.update(visible=True)}
417
- else:
418
- return {custom_color: gr.update(visible=False)}
 
 
 
 
419
 
420
- def change_size_mode(size_option_item):
421
- if (
422
- size_option_item == "自定义尺寸"
423
- or size_option_item == "Custom Size"
424
- ):
425
  return {
426
  custom_size: gr.update(visible=True),
427
  size_list_row: gr.update(visible=False),
428
  }
429
- elif (
430
- size_option_item == "只换底"
431
- or size_option_item == "Only Change Background"
432
- ):
433
  return {
434
  custom_size: gr.update(visible=False),
435
  size_list_row: gr.update(visible=False),
@@ -440,11 +513,15 @@ def create_ui(
440
  size_list_row: gr.update(visible=True),
441
  }
442
 
443
- def change_image_kb(image_kb_option):
444
- if image_kb_option == "自定义" or image_kb_option == "Custom":
445
- return {custom_image_kb: gr.update(visible=True)}
446
- else:
447
- return {custom_image_kb: gr.update(visible=False)}
 
 
 
 
448
 
449
  # ---------------- 绑定事件 ----------------
450
  # 语言切换
@@ -483,21 +560,37 @@ def create_ui(
483
  matting_image_accordion,
484
  beauty_parameter_tab,
485
  whitening_option,
 
 
 
 
 
 
486
  ],
487
  )
488
 
489
  color_options.input(
490
- change_color, inputs=[color_options], outputs=[custom_color]
 
 
491
  )
492
 
493
  mode_options.input(
494
  change_size_mode,
495
- inputs=[mode_options],
496
  outputs=[custom_size, size_list_row],
497
  )
498
 
499
  image_kb_options.input(
500
- change_image_kb, inputs=[image_kb_options], outputs=[custom_image_kb]
 
 
 
 
 
 
 
 
501
  )
502
 
503
  img_but.click(
@@ -528,6 +621,12 @@ def create_ui(
528
  head_measure_ratio_option,
529
  top_distance_option,
530
  whitening_option,
 
 
 
 
 
 
531
  ],
532
  outputs=[
533
  img_output_standard,
 
21
  root_dir: str,
22
  human_matting_models: list,
23
  face_detect_models: list,
24
+ language: list,
25
  ):
26
+ DEFAULT_LANG = language[0]
27
  DEFAULT_HUMAN_MATTING_MODEL = "modnet_photographic_portrait_matting"
28
  DEFAULT_FACE_DETECT_MODEL = "retinaface-resnet50"
29
 
 
39
  with demo:
40
  gr.HTML(load_description(os.path.join(root_dir, "assets/title.md")))
41
  with gr.Row():
42
+ # ------------------------ 左半边 UI ------------------------
43
  with gr.Column():
44
  img_input = gr.Image(height=400)
45
 
46
  with gr.Row():
47
  # 语言选择器
 
48
  language_options = gr.Dropdown(
49
  choices=language,
50
  label="Language",
 
63
  value=human_matting_models[0],
64
  )
65
 
66
+ # TAB1 - 关键参数 ------------------------------------------------
67
  with gr.Tab(
68
  LOCALES["key_param"][DEFAULT_LANG]["label"]
69
  ) as key_parameter_tab:
 
106
  value=LOCALES["render_mode"][DEFAULT_LANG]["choices"][0],
107
  )
108
 
109
+ # TAB2 - 高级参数 ------------------------------------------------
110
  with gr.Tab(
111
  LOCALES["advance_param"][DEFAULT_LANG]["label"]
112
  ) as advance_parameter_tab:
 
133
  value=LOCALES["image_kb"][DEFAULT_LANG]["choices"][0],
134
  )
135
 
136
+ custom_image_kb_size = gr.Slider(
137
+ minimum=10,
138
+ maximum=1000,
139
+ value=50,
140
+ label=LOCALES["image_kb_size"][DEFAULT_LANG]["label"],
141
+ interactive=True,
142
+ visible=False,
143
+ )
144
 
145
+ image_dpi_options = gr.Radio(
146
+ choices=LOCALES["image_dpi"][DEFAULT_LANG]["choices"],
147
+ label=LOCALES["image_dpi"][DEFAULT_LANG]["label"],
148
+ value=LOCALES["image_dpi"][DEFAULT_LANG]["choices"][0],
149
+ )
150
+ custom_image_dpi_size = gr.Slider(
151
+ minimum=72,
152
+ maximum=600,
153
+ value=300,
154
+ label=LOCALES["image_dpi_size"][DEFAULT_LANG]["label"],
155
+ interactive=True,
156
+ visible=False,
157
+ )
158
+
159
+ # TAB3 - 美颜 ------------------------------------------------
160
  with gr.Tab(
161
  LOCALES["beauty_tab"][DEFAULT_LANG]["label"]
162
  ) as beauty_parameter_tab:
163
+ # 美白组件
164
  whitening_option = gr.Slider(
165
  label=LOCALES["whitening_strength"][DEFAULT_LANG]["label"],
166
  minimum=0,
 
170
  interactive=True,
171
  )
172
 
173
+ with gr.Row():
174
+ # 亮度组件
175
+ brightness_option = gr.Slider(
176
+ label=LOCALES["brightness_strength"][DEFAULT_LANG]["label"],
177
+ minimum=-5,
178
+ maximum=25,
179
+ value=0,
180
+ step=1,
181
+ interactive=True,
182
+ )
183
+ # 对比度组件
184
+ contrast_option = gr.Slider(
185
+ label=LOCALES["contrast_strength"][DEFAULT_LANG]["label"],
186
+ minimum=-10,
187
+ maximum=50,
188
+ value=0,
189
+ step=1,
190
+ interactive=True,
191
+ )
192
+ # 饱和度组件
193
+ saturation_option = gr.Slider(
194
+ label=LOCALES["saturation_strength"][DEFAULT_LANG]["label"],
195
+ minimum=-10,
196
+ maximum=50,
197
+ value=0,
198
+ step=1,
199
+ interactive=True,
200
+ )
201
+
202
+ # 锐化组件
203
+ sharpen_option = gr.Slider(
204
+ label=LOCALES["sharpen_strength"][DEFAULT_LANG]["label"],
205
+ minimum=0,
206
+ maximum=5,
207
+ value=0,
208
+ step=1,
209
+ interactive=True,
210
+ )
211
+
212
+ # TAB4 - 水印 ------------------------------------------------
213
  with gr.Tab(
214
  LOCALES["watermark_tab"][DEFAULT_LANG]["label"]
215
  ) as watermark_parameter_tab:
 
463
  whitening_option: gr.update(
464
  label=LOCALES["whitening_strength"][language]["label"]
465
  ),
466
+ image_dpi_options: gr.update(
467
+ label=LOCALES["image_dpi"][language]["label"],
468
+ choices=LOCALES["image_dpi"][language]["choices"],
469
+ value=LOCALES["image_dpi"][language]["choices"][0],
470
+ ),
471
+ custom_image_dpi_size: gr.update(
472
+ label=LOCALES["image_dpi"][language]["label"]
473
+ ),
474
+ brightness_option: gr.update(
475
+ label=LOCALES["brightness_strength"][language]["label"]
476
+ ),
477
+ contrast_option: gr.update(
478
+ label=LOCALES["contrast_strength"][language]["label"]
479
+ ),
480
+ sharpen_option: gr.update(
481
+ label=LOCALES["sharpen_strength"][language]["label"]
482
+ ),
483
+ saturation_option: gr.update(
484
+ label=LOCALES["saturation_strength"][language]["label"]
485
+ ),
486
  }
487
 
488
+ def change_visibility(option, lang, locales_key, custom_component):
489
+ return {
490
+ custom_component: gr.update(
491
+ visible=option == LOCALES[locales_key][lang]["choices"][-1]
492
+ )
493
+ }
494
+
495
+ def change_color(colors, lang):
496
+ return change_visibility(colors, lang, "bg_color", custom_color)
497
 
498
+ def change_size_mode(size_option_item, lang):
499
+ choices = LOCALES["size_mode"][lang]["choices"]
500
+ if size_option_item == choices[2]:
 
 
501
  return {
502
  custom_size: gr.update(visible=True),
503
  size_list_row: gr.update(visible=False),
504
  }
505
+ elif size_option_item == choices[1]:
 
 
 
506
  return {
507
  custom_size: gr.update(visible=False),
508
  size_list_row: gr.update(visible=False),
 
513
  size_list_row: gr.update(visible=True),
514
  }
515
 
516
+ def change_image_kb(image_kb_option, lang):
517
+ return change_visibility(
518
+ image_kb_option, lang, "image_kb", custom_image_kb_size
519
+ )
520
+
521
+ def change_image_dpi(image_dpi_option, lang):
522
+ return change_visibility(
523
+ image_dpi_option, lang, "image_dpi", custom_image_dpi_size
524
+ )
525
 
526
  # ---------------- 绑定事件 ----------------
527
  # 语言切换
 
560
  matting_image_accordion,
561
  beauty_parameter_tab,
562
  whitening_option,
563
+ image_dpi_options,
564
+ custom_image_dpi_size,
565
+ brightness_option,
566
+ contrast_option,
567
+ sharpen_option,
568
+ saturation_option,
569
  ],
570
  )
571
 
572
  color_options.input(
573
+ change_color,
574
+ inputs=[color_options, language_options],
575
+ outputs=[custom_color],
576
  )
577
 
578
  mode_options.input(
579
  change_size_mode,
580
+ inputs=[mode_options, language_options],
581
  outputs=[custom_size, size_list_row],
582
  )
583
 
584
  image_kb_options.input(
585
+ change_image_kb,
586
+ inputs=[image_kb_options, language_options],
587
+ outputs=[custom_image_kb_size],
588
+ )
589
+
590
+ image_dpi_options.input(
591
+ change_image_dpi,
592
+ inputs=[image_dpi_options, language_options],
593
+ outputs=[custom_image_dpi_size],
594
  )
595
 
596
  img_but.click(
 
621
  head_measure_ratio_option,
622
  top_distance_option,
623
  whitening_option,
624
+ image_dpi_options,
625
+ custom_image_dpi_size,
626
+ brightness_option,
627
+ contrast_option,
628
+ sharpen_option,
629
+ saturation_option,
630
  ],
631
  outputs=[
632
  img_output_standard,
hivision/creator/__init__.py CHANGED
@@ -13,8 +13,9 @@ import hivision.creator.utils as U
13
  from .context import Context, ContextHandler, Params, Result
14
  from .human_matting import extract_human
15
  from .face_detector import detect_face_mtcnn
16
- from hivision.plugin.beauty.whitening import make_whitening
17
  from .photo_adjuster import adjust_photo
 
18
 
19
 
20
  class IDCreator:
@@ -43,7 +44,7 @@ class IDCreator:
43
  # 处理者
44
  self.matting_handler: ContextHandler = extract_human
45
  self.detection_handler: ContextHandler = detect_face_mtcnn
46
-
47
  # 上下文
48
  self.ctx = None
49
 
@@ -58,6 +59,10 @@ class IDCreator:
58
  head_top_range: float = (0.12, 0.1),
59
  face: Tuple[int, int, int, int] = None,
60
  whitening_strength: int = 0,
 
 
 
 
61
  ) -> Result:
62
  """
63
  证件照处理函数
@@ -70,7 +75,9 @@ class IDCreator:
70
  :param head_top_range: 头距离顶部的比例(max,min)
71
  :param face: 人脸坐标
72
  :param whitening_strength: 美白强度
73
-
 
 
74
  :return: 返回处理后的证件照和一系列参数
75
  """
76
  # 0.初始化上下文
@@ -83,7 +90,12 @@ class IDCreator:
83
  crop_only=crop_only,
84
  face=face,
85
  whitening_strength=whitening_strength,
 
 
 
 
86
  )
 
87
  self.ctx = Context(params)
88
  ctx = self.ctx
89
  ctx.processing_image = image
@@ -93,12 +105,6 @@ class IDCreator:
93
  ctx.origin_image = ctx.processing_image.copy()
94
  self.before_all and self.before_all(ctx)
95
 
96
- # 美白
97
- if ctx.params.whitening_strength > 0:
98
- ctx.processing_image = make_whitening(
99
- ctx.processing_image, ctx.params.whitening_strength
100
- )
101
-
102
  # 1. 人像抠图
103
  if not ctx.params.crop_only:
104
  # 调用抠图工作流
@@ -107,6 +113,9 @@ class IDCreator:
107
  else:
108
  ctx.matting_image = ctx.processing_image
109
 
 
 
 
110
  # 如果仅换底,则直接返回抠图结果
111
  if ctx.params.change_bg_only:
112
  ctx.result = Result(
 
13
  from .context import Context, ContextHandler, Params, Result
14
  from .human_matting import extract_human
15
  from .face_detector import detect_face_mtcnn
16
+ from hivision.plugin.beauty.handler import beauty_face
17
  from .photo_adjuster import adjust_photo
18
+ import cv2
19
 
20
 
21
  class IDCreator:
 
44
  # 处理者
45
  self.matting_handler: ContextHandler = extract_human
46
  self.detection_handler: ContextHandler = detect_face_mtcnn
47
+ self.beauty_handler: ContextHandler = beauty_face
48
  # 上下文
49
  self.ctx = None
50
 
 
59
  head_top_range: float = (0.12, 0.1),
60
  face: Tuple[int, int, int, int] = None,
61
  whitening_strength: int = 0,
62
+ brightness_strength: int = 0,
63
+ contrast_strength: int = 0,
64
+ sharpen_strength: int = 0,
65
+ saturation_strength: int = 0,
66
  ) -> Result:
67
  """
68
  证件照处理函数
 
75
  :param head_top_range: 头距离顶部的比例(max,min)
76
  :param face: 人脸坐标
77
  :param whitening_strength: 美白强度
78
+ :param brightness_strength: 亮度强度
79
+ :param contrast_strength: 对比度强度
80
+ :param sharpen_strength: 锐化强度
81
  :return: 返回处理后的证件照和一系列参数
82
  """
83
  # 0.初始化上下文
 
90
  crop_only=crop_only,
91
  face=face,
92
  whitening_strength=whitening_strength,
93
+ brightness_strength=brightness_strength,
94
+ contrast_strength=contrast_strength,
95
+ sharpen_strength=sharpen_strength,
96
+ saturation_strength=saturation_strength,
97
  )
98
+
99
  self.ctx = Context(params)
100
  ctx = self.ctx
101
  ctx.processing_image = image
 
105
  ctx.origin_image = ctx.processing_image.copy()
106
  self.before_all and self.before_all(ctx)
107
 
 
 
 
 
 
 
108
  # 1. 人像抠图
109
  if not ctx.params.crop_only:
110
  # 调用抠图工作流
 
113
  else:
114
  ctx.matting_image = ctx.processing_image
115
 
116
+ # 2. 美颜
117
+ self.beauty_handler(ctx)
118
+
119
  # 如果仅换底,则直接返回抠图结果
120
  if ctx.params.change_bg_only:
121
  ctx.result = Result(
hivision/creator/context.py CHANGED
@@ -22,6 +22,10 @@ class Params:
22
  head_top_range: float = (0.12, 0.1),
23
  face: Tuple[int, int, int, int] = None,
24
  whitening_strength: int = 0,
 
 
 
 
25
  ):
26
  self.__size = size
27
  self.__change_bg_only = change_bg_only
@@ -31,6 +35,10 @@ class Params:
31
  self.__head_top_range = head_top_range
32
  self.__face = face
33
  self.__whitening_strength = whitening_strength
 
 
 
 
34
 
35
  @property
36
  def size(self):
@@ -64,6 +72,22 @@ class Params:
64
  def whitening_strength(self):
65
  return self.__whitening_strength
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  class Result:
69
  def __init__(
 
22
  head_top_range: float = (0.12, 0.1),
23
  face: Tuple[int, int, int, int] = None,
24
  whitening_strength: int = 0,
25
+ brightness_strength: int = 0,
26
+ contrast_strength: int = 0,
27
+ sharpen_strength: int = 0,
28
+ saturation_strength: int = 0,
29
  ):
30
  self.__size = size
31
  self.__change_bg_only = change_bg_only
 
35
  self.__head_top_range = head_top_range
36
  self.__face = face
37
  self.__whitening_strength = whitening_strength
38
+ self.__brightness_strength = brightness_strength
39
+ self.__contrast_strength = contrast_strength
40
+ self.__sharpen_strength = sharpen_strength
41
+ self.__saturation_strength = saturation_strength
42
 
43
  @property
44
  def size(self):
 
72
  def whitening_strength(self):
73
  return self.__whitening_strength
74
 
75
+ @property
76
+ def brightness_strength(self):
77
+ return self.__brightness_strength
78
+
79
+ @property
80
+ def contrast_strength(self):
81
+ return self.__contrast_strength
82
+
83
+ @property
84
+ def sharpen_strength(self):
85
+ return self.__sharpen_strength
86
+
87
+ @property
88
+ def saturation_strength(self):
89
+ return self.__saturation_strength
90
+
91
 
92
  class Result:
93
  def __init__(
hivision/creator/photo_adjuster.py CHANGED
@@ -22,7 +22,7 @@ def adjust_photo(ctx: Context):
22
  params = ctx.params
23
  x, y = face_rect[0], face_rect[1]
24
  w, h = face_rect[2], face_rect[3]
25
- height, width = ctx.processing_image.shape[:2]
26
  width_height_ratio = standard_size[0] / standard_size[1]
27
  # Step2. 计算高级参数
28
  face_center = (x + w / 2, y + h / 2) # 面部中心坐标
@@ -46,7 +46,7 @@ def adjust_photo(ctx: Context):
46
  x2 = x1 + crop_size[1]
47
 
48
  # Step3, 裁剪框的调整
49
- cut_image = IDphotos_cut(x1, y1, x2, y2, ctx.processing_image)
50
  cut_image = cv2.resize(cut_image, (crop_size[1], crop_size[0]))
51
  y_top, y_bottom, x_left, x_right = U.get_box(
52
  cut_image.astype(np.uint8), model=2, correction_factor=0
@@ -85,7 +85,7 @@ def adjust_photo(ctx: Context):
85
  y1 + cut_value_top + status_top * move_value,
86
  x2 - x_right,
87
  y2 - cut_value_top + status_top * move_value,
88
- ctx.processing_image,
89
  )
90
 
91
  # 换装参数准备
 
22
  params = ctx.params
23
  x, y = face_rect[0], face_rect[1]
24
  w, h = face_rect[2], face_rect[3]
25
+ height, width = ctx.matting_image.shape[:2]
26
  width_height_ratio = standard_size[0] / standard_size[1]
27
  # Step2. 计算高级参数
28
  face_center = (x + w / 2, y + h / 2) # 面部中心坐标
 
46
  x2 = x1 + crop_size[1]
47
 
48
  # Step3, 裁剪框的调整
49
+ cut_image = IDphotos_cut(x1, y1, x2, y2, ctx.matting_image)
50
  cut_image = cv2.resize(cut_image, (crop_size[1], crop_size[0]))
51
  y_top, y_bottom, x_left, x_right = U.get_box(
52
  cut_image.astype(np.uint8), model=2, correction_factor=0
 
85
  y1 + cut_value_top + status_top * move_value,
86
  x2 - x_right,
87
  y2 - cut_value_top + status_top * move_value,
88
+ ctx.matting_image,
89
  )
90
 
91
  # 换装参数准备
hivision/plugin/beauty/base_adjust.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 亮度、对比度、锐化、饱和度调整模块
3
+ """
4
+
5
+ import cv2
6
+ import numpy as np
7
+
8
+
9
+ def adjust_brightness_contrast_sharpen_saturation(
10
+ image,
11
+ brightness_factor=0,
12
+ contrast_factor=0,
13
+ sharpen_strength=0,
14
+ saturation_factor=0,
15
+ ):
16
+ """
17
+ 调整图像的亮度、对比度、锐度和饱和度。
18
+
19
+ 参数:
20
+ image (numpy.ndarray): 输入的图像数组。
21
+ brightness_factor (float): 亮度调整因子。大于0增加亮度,小于0降低亮度。
22
+ contrast_factor (float): 对比度调整因子。大于0增加对比度,小于0降低对比度。
23
+ sharpen_strength (float): 锐化强度。
24
+ saturation_factor (float): 饱和度调整因子。大于0增加饱和度,小于0降低饱和度。
25
+
26
+ 返回:
27
+ numpy.ndarray: 调整后的图像。
28
+ """
29
+ if (
30
+ brightness_factor == 0
31
+ and contrast_factor == 0
32
+ and sharpen_strength == 0
33
+ and saturation_factor == 0
34
+ ):
35
+ return image.copy()
36
+
37
+ adjusted_image = image.copy()
38
+
39
+ # 调整饱和度
40
+ if saturation_factor != 0:
41
+ adjusted_image = adjust_saturation(adjusted_image, saturation_factor)
42
+
43
+ # 调整亮度和对比度
44
+ alpha = 1.0 + (contrast_factor / 100.0)
45
+ beta = brightness_factor
46
+ adjusted_image = cv2.convertScaleAbs(adjusted_image, alpha=alpha, beta=beta)
47
+
48
+ # 增强锐化
49
+ adjusted_image = sharpen_image(adjusted_image, sharpen_strength)
50
+
51
+ return adjusted_image
52
+
53
+
54
+ def adjust_saturation(image, saturation_factor):
55
+ """
56
+ 调整图像的饱和度。
57
+
58
+ 参数:
59
+ image (numpy.ndarray): 输入的图像数组。
60
+ saturation_factor (float): 饱和度调整因子。大于0增加饱和度,小于0降低饱和度。
61
+
62
+ 返回:
63
+ numpy.ndarray: 调整后的图像。
64
+ """
65
+ hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
66
+ h, s, v = cv2.split(hsv)
67
+ s = s.astype(np.float32)
68
+ s = s + s * (saturation_factor / 100.0)
69
+ s = np.clip(s, 0, 255).astype(np.uint8)
70
+ hsv = cv2.merge([h, s, v])
71
+ return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
72
+
73
+
74
+ def sharpen_image(image, strength=0):
75
+ """
76
+ 对图像进行锐化处理。
77
+
78
+ 参数:
79
+ image (numpy.ndarray): 输入的图像数组。
80
+ strength (float): 锐化强度,范围建议为0-5。0表示不进行锐化。
81
+
82
+ 返回:
83
+ numpy.ndarray: 锐化后的图像。
84
+ """
85
+ print(f"Sharpen strength: {strength}")
86
+ if strength == 0:
87
+ return image.copy()
88
+
89
+ strength = strength * 20
90
+ kernel_strength = 1 + (strength / 500)
91
+
92
+ kernel = (
93
+ np.array([[-0.5, -0.5, -0.5], [-0.5, 5, -0.5], [-0.5, -0.5, -0.5]])
94
+ * kernel_strength
95
+ )
96
+
97
+ sharpened = cv2.filter2D(image, -1, kernel)
98
+ sharpened = np.clip(sharpened, 0, 255).astype(np.uint8)
99
+
100
+ alpha = strength / 200
101
+ blended = cv2.addWeighted(image, 1 - alpha, sharpened, alpha, 0)
102
+
103
+ return blended
104
+
105
+
106
+ # Gradio接口
107
+ def base_adjustment(image, brightness, contrast, sharpen, saturation):
108
+ adjusted = adjust_brightness_contrast_sharpen_saturation(
109
+ image, brightness, contrast, sharpen, saturation
110
+ )
111
+ return adjusted
112
+
113
+
114
+ if __name__ == "__main__":
115
+ import gradio as gr
116
+
117
+ iface = gr.Interface(
118
+ fn=base_adjustment,
119
+ inputs=[
120
+ gr.Image(label="Input Image", height=400),
121
+ gr.Slider(
122
+ minimum=-20,
123
+ maximum=20,
124
+ value=0,
125
+ step=1,
126
+ label="Brightness",
127
+ ),
128
+ gr.Slider(
129
+ minimum=-100,
130
+ maximum=100,
131
+ value=0,
132
+ step=1,
133
+ label="Contrast",
134
+ ),
135
+ gr.Slider(
136
+ minimum=0,
137
+ maximum=5,
138
+ value=0,
139
+ step=1,
140
+ label="Sharpen",
141
+ ),
142
+ gr.Slider(
143
+ minimum=-100,
144
+ maximum=100,
145
+ value=0,
146
+ step=1,
147
+ label="Saturation",
148
+ ),
149
+ ],
150
+ outputs=gr.Image(label="Adjusted Image"),
151
+ title="Image Adjustment",
152
+ description="Adjust the brightness, contrast, sharpness, and saturation of an image using sliders.",
153
+ )
154
+ iface.launch()
hivision/plugin/beauty/grind_skin.py CHANGED
@@ -1,20 +1,50 @@
1
- """
2
- @author: cuny
3
- @file: GrindSkin.py
4
- @time: 2022/7/2 14:44
5
- @description:
6
- 磨皮算法
7
- """
8
-
9
  import cv2
10
  import numpy as np
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
 
13
  def grindSkin(src, grindDegree: int = 3, detailDegree: int = 1, strength: int = 9):
14
  """
15
- Dest =(Src * (100 - Opacity) + (Src + 2 * GaussBlur(EPFFilter(Src) - Src)) * Opacity) /100
16
- 人像磨皮方案,后续会考虑使用一些皮肤区域检测算法来实现仅皮肤区域磨皮,增加算法的精细程度——或者使用人脸关键点
17
- https://www.cnblogs.com/Imageshop/p/4709710.html
18
  Args:
19
  src: 原图
20
  grindDegree: 磨皮程度调节参数
@@ -28,8 +58,8 @@ def grindSkin(src, grindDegree: int = 3, detailDegree: int = 1, strength: int =
28
  return src
29
  dst = src.copy()
30
  opacity = min(10.0, strength) / 10.0
31
- dx = grindDegree * 5 # 双边滤波参数之一
32
- fc = grindDegree * 12.5 # 双边滤波参数之一
33
  temp1 = cv2.bilateralFilter(src[:, :, :3], dx, fc, fc)
34
  temp2 = cv2.subtract(temp1, src[:, :, :3])
35
  temp3 = cv2.GaussianBlur(temp2, (2 * detailDegree - 1, 2 * detailDegree - 1), 0)
@@ -38,7 +68,49 @@ def grindSkin(src, grindDegree: int = 3, detailDegree: int = 1, strength: int =
38
  return dst
39
 
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  if __name__ == "__main__":
42
- input_image = cv2.imread("test_image/7.jpg")
43
- output_image = grindSkin(src=input_image)
44
- cv2.imwrite("grindSkinCompare.png", np.hstack((input_image, output_image)))
 
1
+ # Required Libraries
 
 
 
 
 
 
 
2
  import cv2
3
  import numpy as np
4
+ import gradio as gr
5
+
6
+
7
+ def annotate_image(image, grind_degree, detail_degree, strength):
8
+ """Annotates the image with parameters in the lower-left corner."""
9
+ font = cv2.FONT_HERSHEY_SIMPLEX
10
+ font_scale = 0.5
11
+ color = (0, 0, 255)
12
+ thickness = 1
13
+ line_type = cv2.LINE_AA
14
+
15
+ # Text positions
16
+ y_offset = 20
17
+ x_offset = 10
18
+ y_base = image.shape[0] - 10
19
+
20
+ # Define each line of the annotation
21
+ lines = [
22
+ f"Grind Degree: {grind_degree}",
23
+ f"Detail Degree: {detail_degree}",
24
+ f"Strength: {strength}",
25
+ ]
26
+
27
+ # Draw the text lines on the image
28
+ for i, line in enumerate(lines):
29
+ y_position = y_base - (i * y_offset)
30
+ cv2.putText(
31
+ image,
32
+ line,
33
+ (x_offset, y_position),
34
+ font,
35
+ font_scale,
36
+ color,
37
+ thickness,
38
+ line_type,
39
+ )
40
+
41
+ return image
42
 
43
 
44
  def grindSkin(src, grindDegree: int = 3, detailDegree: int = 1, strength: int = 9):
45
  """
46
+ Dest =(Src * (100 - Opacity) + (Src + 2 * GaussBlur(EPFFilter(Src) - Src)) * Opacity) / 100
47
+ 人像磨皮方案
 
48
  Args:
49
  src: 原图
50
  grindDegree: 磨皮程度调节参数
 
58
  return src
59
  dst = src.copy()
60
  opacity = min(10.0, strength) / 10.0
61
+ dx = grindDegree * 5
62
+ fc = grindDegree * 12.5
63
  temp1 = cv2.bilateralFilter(src[:, :, :3], dx, fc, fc)
64
  temp2 = cv2.subtract(temp1, src[:, :, :3])
65
  temp3 = cv2.GaussianBlur(temp2, (2 * detailDegree - 1, 2 * detailDegree - 1), 0)
 
68
  return dst
69
 
70
 
71
+ def process_image(input_img, grind_degree, detail_degree, strength):
72
+ # Reading the image using OpenCV
73
+ img = cv2.cvtColor(input_img, cv2.COLOR_RGB2BGR)
74
+ # Processing the image
75
+ output_img = grindSkin(img, grind_degree, detail_degree, strength)
76
+ # Annotating the processed image with parameters
77
+ output_img_annotated = annotate_image(
78
+ output_img.copy(), grind_degree, detail_degree, strength
79
+ )
80
+ # Horizontal stacking of input and processed images
81
+ combined_img = cv2.hconcat([img, output_img_annotated])
82
+ # Convert the combined image back to RGB for display
83
+ combined_img_rgb = cv2.cvtColor(combined_img, cv2.COLOR_BGR2RGB)
84
+ return combined_img_rgb
85
+
86
+
87
+ with gr.Blocks(title="Skin Grinding") as iface:
88
+ gr.Markdown("## Skin Grinding Application")
89
+
90
+ with gr.Row():
91
+ image_input = gr.Image(type="numpy", label="Input Image")
92
+ image_output = gr.Image(label="Output Image")
93
+
94
+ grind_degree_slider = gr.Slider(
95
+ minimum=1, maximum=10, value=3, step=1, label="Grind Degree"
96
+ )
97
+ detail_degree_slider = gr.Slider(
98
+ minimum=1, maximum=10, value=1, step=1, label="Detail Degree"
99
+ )
100
+ strength_slider = gr.Slider(
101
+ minimum=0, maximum=10, value=9, step=1, label="Strength"
102
+ )
103
+
104
+ gr.Button("Process Image").click(
105
+ fn=process_image,
106
+ inputs=[
107
+ image_input,
108
+ grind_degree_slider,
109
+ detail_degree_slider,
110
+ strength_slider,
111
+ ],
112
+ outputs=image_output,
113
+ )
114
+
115
  if __name__ == "__main__":
116
+ iface.launch()
 
 
hivision/plugin/beauty/handler.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ from hivision.creator.context import Context
3
+ from hivision.plugin.beauty.whitening import make_whitening
4
+ from hivision.plugin.beauty.base_adjust import (
5
+ adjust_brightness_contrast_sharpen_saturation,
6
+ )
7
+
8
+
9
+ def beauty_face(ctx: Context):
10
+ """
11
+ 对人脸进行美颜处理
12
+ 1. 美白
13
+ 2. 亮度
14
+
15
+ :param ctx: Context对象,包含处理参数和图像
16
+ """
17
+ middle_image = ctx.origin_image.copy()
18
+ processed = False
19
+
20
+ # 如果美白强度大于0,进行美白处理
21
+ if ctx.params.whitening_strength > 0:
22
+ middle_image = make_whitening(middle_image, ctx.params.whitening_strength)
23
+ processed = True
24
+
25
+ # 如果亮度、对比度、锐化强度不为0,进行亮度、对比度、锐化处理
26
+ if (
27
+ ctx.params.brightness_strength != 0
28
+ or ctx.params.contrast_strength != 0
29
+ or ctx.params.sharpen_strength != 0
30
+ or ctx.params.saturation_strength != 0
31
+ ):
32
+ middle_image = adjust_brightness_contrast_sharpen_saturation(
33
+ middle_image,
34
+ ctx.params.brightness_strength,
35
+ ctx.params.contrast_strength,
36
+ ctx.params.sharpen_strength,
37
+ ctx.params.saturation_strength,
38
+ )
39
+ processed = True
40
+
41
+ # 如果进行了美颜处理,更新matting_image
42
+ if processed:
43
+ # 分离中间图像的BGR通道
44
+ b, g, r = cv2.split(middle_image)
45
+ # 从原始matting_image中获取alpha通道
46
+ _, _, _, alpha = cv2.split(ctx.matting_image)
47
+ # 合并处理后的BGR通道和原始alpha通道
48
+ ctx.matting_image = cv2.merge((b, g, r, alpha))
hivision/plugin/beauty/whitening.py CHANGED
@@ -52,8 +52,16 @@ make_whiter = MakeWhiter(default_lut)
52
 
53
  def make_whitening(image, strength):
54
  image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
55
- output_image = make_whiter.run(image, strength)
56
- return cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB)
 
 
 
 
 
 
 
 
57
 
58
 
59
  def make_whitening_png(image, strength):
@@ -71,10 +79,10 @@ def make_whitening_png(image, strength):
71
  # 启动Gradio应用
72
  if __name__ == "__main__":
73
  demo = gr.Interface(
74
- fn=make_whitening_png,
75
  inputs=[
76
  gr.Image(type="pil", image_mode="RGBA", label="Input Image"),
77
- gr.Slider(0, 10, step=1, label="Whitening Strength"),
78
  ],
79
  outputs=gr.Image(type="pil"),
80
  title="Image Whitening Demo",
 
52
 
53
  def make_whitening(image, strength):
54
  image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
55
+
56
+ iteration = strength // 10
57
+ bias = strength % 10
58
+
59
+ for i in range(iteration):
60
+ image = make_whiter.run(image, 10)
61
+
62
+ image = make_whiter.run(image, bias)
63
+
64
+ return cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
65
 
66
 
67
  def make_whitening_png(image, strength):
 
79
  # 启动Gradio应用
80
  if __name__ == "__main__":
81
  demo = gr.Interface(
82
+ fn=make_whitening,
83
  inputs=[
84
  gr.Image(type="pil", image_mode="RGBA", label="Input Image"),
85
+ gr.Slider(0, 30, step=1, label="Whitening Strength"),
86
  ],
87
  outputs=gr.Image(type="pil"),
88
  title="Image Whitening Demo",
hivision/utils.py CHANGED
@@ -1,12 +1,5 @@
1
  #!/usr/bin/env python
2
  # -*- coding: utf-8 -*-
3
- r"""
4
- @DATE: 2024/9/5 21:52
5
- @File: utils.py
6
- @IDE: pycharm
7
- @Description:
8
- hivision提供的工具函数
9
- """
10
  from PIL import Image
11
  import io
12
  import numpy as np
@@ -15,7 +8,30 @@ import base64
15
  from hivision.plugin.watermark import Watermarker, WatermarkerStyles
16
 
17
 
18
- def resize_image_to_kb(input_image, output_image_path, target_size_kb):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  """
20
  Resize an image to a target size in KB.
21
  将图像调整大小至目标文件大小(KB)。
@@ -47,7 +63,7 @@ def resize_image_to_kb(input_image, output_image_path, target_size_kb):
47
  img_byte_arr = io.BytesIO()
48
 
49
  # Save the image to the BytesIO object with the current quality
50
- img.save(img_byte_arr, format="JPEG", quality=quality)
51
 
52
  # Get the size of the image in KB
53
  img_size_kb = len(img_byte_arr.getvalue()) / 1024
@@ -145,14 +161,14 @@ def resize_image_to_kb_base64(input_image, target_size_kb, mode="exact"):
145
 
146
  # Encode the image data to base64
147
  img_base64 = base64.b64encode(img_byte_arr.getvalue()).decode("utf-8")
148
- return img_base64
149
 
150
 
151
  def numpy_2_base64(img: np.ndarray) -> str:
152
  _, buffer = cv2.imencode(".png", img)
153
  base64_image = base64.b64encode(buffer).decode("utf-8")
154
 
155
- return base64_image
156
 
157
 
158
  def base64_2_numpy(base64_image: str) -> np.ndarray:
@@ -258,7 +274,13 @@ def add_background(input_image, bgr=(0, 0, 0), mode="pure_color"):
258
  :return: output: 合成好的输出图像
259
  """
260
  height, width = input_image.shape[0], input_image.shape[1]
261
- b, g, r, a = cv2.split(input_image)
 
 
 
 
 
 
262
  a_cal = a / 255
263
  if mode == "pure_color":
264
  # 纯色填充
 
1
  #!/usr/bin/env python
2
  # -*- coding: utf-8 -*-
 
 
 
 
 
 
 
3
  from PIL import Image
4
  import io
5
  import numpy as np
 
8
  from hivision.plugin.watermark import Watermarker, WatermarkerStyles
9
 
10
 
11
+ def save_image_dpi_to_bytes(image, output_image_path, dpi=300):
12
+ """
13
+ 设置图像的DPI(每英寸点数)并返回字节流
14
+
15
+ :param image: numpy.ndarray, 输入的图像数组
16
+ :param output_image_path: Path to save the resized image. 保存调整大小后的图像的路径。
17
+ :param dpi: int, 要设置的DPI值,默认为300
18
+ """
19
+ image = Image.fromarray(image)
20
+ # 创建一个字节流对象
21
+ byte_stream = io.BytesIO()
22
+ # 将图像保存到字节流
23
+ image.save(byte_stream, format="PNG", dpi=(dpi, dpi))
24
+ # 获取字节流的内容
25
+ image_bytes = byte_stream.getvalue()
26
+
27
+ # Save the image to the output path
28
+ with open(output_image_path, "wb") as f:
29
+ f.write(image_bytes)
30
+
31
+ return image_bytes
32
+
33
+
34
+ def resize_image_to_kb(input_image, output_image_path, target_size_kb, dpi=300):
35
  """
36
  Resize an image to a target size in KB.
37
  将图像调整大小至目标文件大小(KB)。
 
63
  img_byte_arr = io.BytesIO()
64
 
65
  # Save the image to the BytesIO object with the current quality
66
+ img.save(img_byte_arr, format="JPEG", quality=quality, dpi=(dpi, dpi))
67
 
68
  # Get the size of the image in KB
69
  img_size_kb = len(img_byte_arr.getvalue()) / 1024
 
161
 
162
  # Encode the image data to base64
163
  img_base64 = base64.b64encode(img_byte_arr.getvalue()).decode("utf-8")
164
+ return "data:image/png;base64," + img_base64
165
 
166
 
167
  def numpy_2_base64(img: np.ndarray) -> str:
168
  _, buffer = cv2.imencode(".png", img)
169
  base64_image = base64.b64encode(buffer).decode("utf-8")
170
 
171
+ return "data:image/png;base64," + base64_image
172
 
173
 
174
  def base64_2_numpy(base64_image: str) -> np.ndarray:
 
274
  :return: output: 合成好的输出图像
275
  """
276
  height, width = input_image.shape[0], input_image.shape[1]
277
+ try:
278
+ b, g, r, a = cv2.split(input_image)
279
+ except ValueError:
280
+ raise ValueError(
281
+ "The input image must have 4 channels. 输入图像必须有4个通道,即透明图像。"
282
+ )
283
+
284
  a_cal = a / 255
285
  if mode == "pure_color":
286
  # 纯色填充