Spaces:
Running
Running
TheEeeeLin
commited on
Commit
•
f8cafb8
1
Parent(s):
cb449af
update
Browse files- assets/title.md +9 -9
- demo/locals.py +314 -2
- demo/processor.py +370 -224
- demo/ui.py +136 -37
- hivision/creator/__init__.py +18 -9
- hivision/creator/context.py +24 -0
- hivision/creator/photo_adjuster.py +3 -3
- hivision/plugin/beauty/base_adjust.py +154 -0
- hivision/plugin/beauty/grind_skin.py +88 -16
- hivision/plugin/beauty/handler.py +48 -0
- hivision/plugin/beauty/whitening.py +12 -4
- hivision/utils.py +34 -12
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>  
|
7 |
-
<a href="https://docs.qq.com/doc/DUkpBdk90eWZFS2JW" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/WeChat-微信-4cb55e"></a>  
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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>  
|
7 |
+
<a href="https://docs.qq.com/doc/DUkpBdk90eWZFS2JW" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/WeChat-微信-4cb55e"></a>  
|
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>  
|
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
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
91 |
-
range_check(custom_color_G),
|
92 |
-
range_check(custom_color_B),
|
93 |
)
|
94 |
else:
|
95 |
-
hex_color =
|
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 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
|
|
|
|
|
|
|
|
|
|
114 |
change_bg_only = (
|
115 |
idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]
|
116 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
)
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
),
|
154 |
-
|
155 |
-
|
156 |
-
#
|
157 |
-
|
158 |
-
(
|
|
|
|
|
|
|
|
|
|
|
159 |
|
160 |
-
|
161 |
-
result_image_hd_png = np.uint8(result_image_hd)
|
162 |
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
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 =
|
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 |
-
#
|
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 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
|
145 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
|
|
|
|
|
|
|
|
419 |
|
420 |
-
def change_size_mode(size_option_item):
|
421 |
-
|
422 |
-
|
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 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
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,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
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.
|
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.
|
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.
|
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 |
-
|
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 |
-
|
56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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=
|
75 |
inputs=[
|
76 |
gr.Image(type="pil", image_mode="RGBA", label="Input Image"),
|
77 |
-
gr.Slider(0,
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
# 纯色填充
|