TheEeeeLin commited on
Commit
23cd1cf
1 Parent(s): 3519dcc
Dockerfile DELETED
@@ -1,35 +0,0 @@
1
- FROM ubuntu:22.04
2
-
3
- # apt换源,安装pip
4
- RUN echo "==> 换成清华源,并更新..." && \
5
- sed -i s@/archive.ubuntu.com/@/mirrors.tuna.tsinghua.edu.cn/@g /etc/apt/sources.list && \
6
- sed -i s@/security.ubuntu.com/@/mirrors.tuna.tsinghua.edu.cn/@g /etc/apt/sources.list && \
7
- apt-get clean && \
8
- apt-get update
9
-
10
- # 安装python3.10
11
- RUN apt-get install -y python3 curl && \
12
- curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
13
- python3 get-pip.py && \
14
- pip3 install -U pip && \
15
- pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
16
-
17
- # 安装ffmpeg等库
18
- RUN apt-get install libpython3.10-dev ffmpeg libgl1-mesa-glx libglib2.0-0 cmake -y && \
19
- pip3 install --no-cache-dir cmake
20
-
21
- WORKDIR /app
22
-
23
- COPY . .
24
-
25
- RUN pip3 install -r requirements.txt && \
26
- pip3 install -r requirements-app.txt
27
-
28
- RUN echo "==> Clean up..." && \
29
- rm -rf ~/.cache/pip
30
-
31
- # 指定工作目录
32
-
33
- EXPOSE 7860
34
-
35
- CMD [ "python3", "app.py", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -7,328 +7,4 @@ sdk: gradio
7
  sdk_version: 4.43.0
8
  app_file: app.py
9
  pinned: true
10
- ---
11
-
12
-
13
- <div align="center">
14
- <h1>HivisionIDPhoto</h1>
15
-
16
- [English](README_EN.md) / 中文 / [日本語](README_JP.md) / [한국어](README_KO.md)
17
-
18
- [![GitHub](https://img.shields.io/static/v1?label=GitHub&message=GitHub&color=black)](https://github.com/xiaolin199912/HivisionIDPhotos)
19
- [![SwanHub Demo](https://swanhub.co/git/repo/SwanHub%2FAuto-README/file/preview?ref=main&path=swanhub.svg)](https://swanhub.co/ZeYiLin/HivisionIDPhotos/demo)
20
- [![zhihu](https://img.shields.io/static/v1?label=知乎&message=知乎&color=blue)](https://zhuanlan.zhihu.com/p/638254028)
21
- [![Spaces](https://img.shields.io/badge/🤗-Open%20in%20Spaces-blue)](https://huggingface.co/spaces/TheEeeeLin/HivisionIDPhotos)
22
- <a href="https://docs.qq.com/doc/DUkpBdk90eWZFS2JW" target="_blank">
23
- <img alt="Static Badge" src="https://img.shields.io/badge/WeChat-微信-4cb55e"></a>
24
-
25
- <a href="https://trendshift.io/repositories/11622" target="_blank"><img src="https://trendshift.io/api/badge/repositories/11622" alt="Zeyi-Lin%2FHivisionIDPhotos | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
26
-
27
- <img src="assets/demoImage.png" width=900>
28
-
29
- </div>
30
-
31
- <br>
32
-
33
- > **相关项目**:
34
- >
35
- > - [SwanLab](https://github.com/SwanHubX/SwanLab):训练人像抠图模型全程用它来分析和监控,以及和实验室同学协作交流,大幅提升了训练效率。
36
-
37
- <br>
38
-
39
- # 🤩 项目更新
40
-
41
- - 在线体验: [![SwanHub Demo](https://img.shields.io/static/v1?label=Demo&message=SwanHub%20Demo&color=blue)](https://swanhub.co/ZeYiLin/HivisionIDPhotos/demo)、[![Spaces](https://img.shields.io/badge/🤗-Open%20in%20Spaces-blue)](https://huggingface.co/spaces/TheEeeeLin/HivisionIDPhotos)
42
-
43
- - 2024.9.5: 更新 [Restful API 文档](docs/api_CN.md)
44
- - 2024.9.2: 更新**调整照片 KB 大小**,[DockerHub](https://hub.docker.com/r/linzeyi/hivision_idphotos/tags)
45
- - 2023.12.1: 更新**API 部署(基于 fastapi)**
46
- - 2023.6.20: 更新**预设尺寸菜单**
47
- - 2023.6.19: 更新**排版照**
48
-
49
- # Overview
50
-
51
- > 🚀 谢谢你对我们的工作感兴趣。您可能还想查看我们在图像领域的其他成果,欢迎来信:[email protected].
52
-
53
- HivisionIDPhoto 旨在开发一种实用的证件照智能制作算法。
54
-
55
- 它利用一套完善的模型工作流程,实现对多种用户拍照场景的识别、抠图与证件照生成。
56
-
57
- **HivisionIDPhoto 可以做到:**
58
-
59
- 1. 轻量级抠图(仅需 **CPU** 即可快速推理)
60
- 2. 根据不同尺寸规格生成不同的标准证件照、六寸排版照
61
- 3. 美颜(waiting)
62
- 4. 智能换正装(waiting)
63
-
64
- <div align="center">
65
- <img src="assets/gradio-image.jpeg" width=900>
66
- </div>
67
-
68
- ---
69
-
70
- 如果 HivisionIDPhoto 对你有帮助,请 star 这个 repo 或推荐给你的朋友,解决证件照应急制作问题!
71
-
72
- <br>
73
-
74
- # 🔧 环境安装与依赖
75
-
76
- - Python >= 3.7(项目主要测试在 python 3.10)
77
- - onnxruntime
78
- - OpenCV
79
- - Option: Linux, Windows, MacOS
80
-
81
- **1. 克隆项目**
82
-
83
- ```bash
84
- git clone https://github.com/Zeyi-Lin/HivisionIDPhotos.git
85
- cd HivisionIDPhotos
86
- ```
87
-
88
- **2. (重要)安装依赖环境**
89
-
90
- > 建议 conda 创建一个 python3.10 虚拟环境后,执行以下命令
91
-
92
- ```bash
93
- pip install -r requirements.txt
94
- pip install -r requirements-app.txt
95
- ```
96
-
97
- **3. 下载权重文件**
98
-
99
- 在我们的[Release](https://github.com/Zeyi-Lin/HivisionIDPhotos/releases/tag/pretrained-model)下载权重文件`hivision_modnet.onnx` (24.7MB),存到项目的`hivision/creator/weights`目录下。
100
-
101
- <br>
102
-
103
- # 🚀 运行 Gradio Demo
104
-
105
- ```bash
106
- python app.py
107
- ```
108
-
109
- 运行程序将生成一个本地 Web 页面,在页面中可完成证件照的操作与交互。
110
-
111
- <br>
112
-
113
- # 🚀 Python 推理
114
-
115
- ## 1. 证件照制作
116
-
117
- 输入 1 张照片,获得 1 张标准证件照和 1 张高清证件照的 4 通道透明 png
118
-
119
- ```python
120
- python inference.py -i demo/images/test.jpg -o ./idphoto.png --height 413 --width 295
121
- ```
122
-
123
- ## 2. 增加底色
124
-
125
- 输入 1 张 4 通道透明 png,获得 1 张增加了底色的图像)
126
-
127
- ```python
128
- python inference.py -t add_background -i ./idphoto.png -o ./idhoto_ab.jpg -c 000000 -k 30
129
- ```
130
-
131
- ## 3. 得到六寸排版照
132
-
133
- 输入 1 张 3 通道照片,获得 1 张六寸排版照
134
-
135
- ```python
136
- python inference.py -t generate_layout_photos -i ./idhoto_ab.jpg -o ./idhoto_layout.jpg --height 413 --width 295 -k 200
137
- ```
138
-
139
- <br>
140
-
141
- # ⚡️ 部署 API 服务
142
-
143
- ## 启动后端
144
-
145
- ```
146
- python deploy_api.py
147
- ```
148
-
149
-
150
- ## 请求 API 服务 - Python Request
151
-
152
- > 请求方式请参考 [API 文档](docs/api_CN.md),含 [cURL](docs/api_CN.md#curl-请求示例)、[Python](docs/api_CN.md#python-请求示例)、[Java](docs/api_CN.md#java-请求示例)、[Javascript](docs/api_CN.md#javascript-请求示例) 请求示例。
153
-
154
- ### 1. 证件照制作
155
-
156
- 输入 1 张照片,获得 1 张标准证件照和 1 张高清证件照的 4 通道透明 png
157
-
158
- ```python
159
- import requests
160
-
161
- url = "http://127.0.0.1:8080/idphoto"
162
- input_image_path = "demo/images/test.jpg"
163
-
164
- files = {"input_image": open(input_image_path, "rb")}
165
- data = {"height": 413, "width": 295}
166
-
167
- response = requests.post(url, files=files, data=data).json()
168
-
169
- # response为一个json格式字典,包含status、image_base64_standard和image_base64_hd三项
170
- print(response)
171
-
172
- ```
173
-
174
- ### 2. 增加底色
175
-
176
- 输入 1 张 4 通道透明 png,获得 1 张增加了底色的图像
177
-
178
- ```python
179
- import requests
180
-
181
- url = "http://127.0.0.1:8080/add_background"
182
- input_image_path = "test.png"
183
-
184
- files = {"input_image": open(input_image_path, "rb")}
185
- data = {"color": '638cce', 'kb': None}
186
-
187
- response = requests.post(url, files=files, data=data).json()
188
-
189
- # response为一个json格式字典,包含status和image_base64
190
- print(response)
191
- ```
192
-
193
- ### 3. 得到六寸排版照
194
-
195
- 输入 1 张 3 通道照片,获得 1 张六寸排版照
196
-
197
- ```python
198
- import requests
199
-
200
- url = "http://127.0.0.1:8080/generate_layout_photos"
201
- input_image_path = "test.jpg"
202
-
203
- files = {"input_image": open(input_image_path, "rb")}
204
- data = {"height": 413, "width": 295, "kb": 200}
205
-
206
- response = requests.post(url, files=files, data=data).json()
207
-
208
- # response为一个json格式字典,包含status和image_base64
209
- print(response)
210
- ```
211
-
212
- <br>
213
-
214
- # 🐳 Docker 部署
215
-
216
- ## 1. 拉取或构建镜像
217
-
218
- > 以下方式三选一
219
-
220
- **方式一:拉取镜像:**
221
-
222
- ```bash
223
- docker pull linzeyi/hivision_idphotos:v1
224
- docker tag linzeyi/hivision_idphotos:v1 hivision_idphotos
225
- ```
226
-
227
- 国内拉取加速:
228
-
229
- ```bash
230
- docker pull registry.cn-hangzhou.aliyuncs.com/swanhub/hivision_idphotos:v1
231
- docker tag registry.cn-hangzhou.aliyuncs.com/swanhub/hivision_idphotos:v1 hivision_idphotos
232
- ```
233
-
234
- **方式二:Dockrfile 直接构建镜像:**
235
-
236
- 在确保将模型权重文件[hivision_modnet.onnx](https://github.com/Zeyi-Lin/HivisionIDPhotos/releases/tag/pretrained-model)放到`hivision/creator/weights`下后,在项目根目录执行:
237
-
238
- ```bash
239
- docker build -t hivision_idphotos .
240
- ```
241
-
242
- **方式三:Docker compose 构建:**
243
-
244
- 确保将模型权重文件 [hivision_modnet.onnx](https://github.com/Zeyi-Lin/HivisionIDPhotos/releases/tag/pretrained-model) 放在`hivision/creator/weights`下后,在项目根目录下执行:
245
-
246
- ```bash
247
- docker compose build
248
- ```
249
-
250
- 镜像打包完成后,运行以下命令启动 Gradio 服务:
251
-
252
- ```bash
253
- docker compose up -d
254
- ```
255
-
256
- ## 2. 运行 Gradio Demo
257
-
258
- 等待镜像封装完毕后,运行以下指令,即可开启 Gradio Demo 服务:
259
-
260
- ```bash
261
- docker run -p 7860:7860 hivision_idphotos
262
- ```
263
-
264
- 在你的本地访问 [http://127.0.0.1:7860](http://127.0.0.1:7860/) 即可使用。
265
-
266
- ## 3. 运行 API 后端服务
267
-
268
- ```bash
269
- docker run -p 8080:8080 hivision_idphotos python3 deploy_api.py
270
- ```
271
-
272
- <br>
273
-
274
- # 🌲 友情链接
275
-
276
- - [HivisionIDPhotos-windows-GUI](https://github.com/zhaoyun0071/HivisionIDPhotos-windows-GUI)
277
-
278
- <br>
279
-
280
- # 📖 引用项目
281
-
282
- 1. MTCNN:
283
-
284
- ```bibtex
285
- @software{ipazc_mtcnn_2021,
286
- author = {ipazc},
287
- title = {{MTCNN}},
288
- url = {https://github.com/ipazc/mtcnn},
289
- year = {2021},
290
- publisher = {GitHub}
291
- }
292
- ```
293
-
294
- 2. ModNet:
295
-
296
- ```bibtex
297
- @software{zhkkke_modnet_2021,
298
- author = {ZHKKKe},
299
- title = {{ModNet}},
300
- url = {https://github.com/ZHKKKe/MODNet},
301
- year = {2021},
302
- publisher = {GitHub}
303
- }
304
- ```
305
-
306
- <br>
307
-
308
- # 💻 开发小贴士
309
-
310
- **1. 如何修改预设尺寸?**
311
-
312
- 修改[size_list_CN.csv](demo/size_list_CN.csv)后再次运行 app.py 即可,其中第一列为尺寸名,第二列为高度,第三列为宽度。
313
-
314
- <br>
315
-
316
- # 📧 联系我们
317
-
318
- 如果您有任何问题,请发邮件至 [email protected]
319
-
320
- <br>
321
-
322
- # 贡献者
323
-
324
- <a href="https://github.com/Zeyi-Lin/HivisionIDPhotos/graphs/contributors">
325
- <img src="https://contrib.rocks/image?repo=Zeyi-Lin/HivisionIDPhotos" />
326
- </a>
327
-
328
- [Zeyi-Lin](https://github.com/Zeyi-Lin)、[SAKURA-CAT](https://github.com/SAKURA-CAT)、[Feudalman](https://github.com/Feudalman)、[swpfY](https://github.com/swpfY)、[Kaikaikaifang](https://github.com/Kaikaikaifang)、[ShaohonChen](https://github.com/ShaohonChen)、[KashiwaByte](https://github.com/KashiwaByte)
329
-
330
- <br>
331
-
332
- # StarHistory
333
-
334
- [![Star History Chart](https://api.star-history.com/svg?repos=Zeyi-Lin/HivisionIDPhotos&type=Date)](https://star-history.com/#Zeyi-Lin/HivisionIDPhotos&Date)
 
7
  sdk_version: 4.43.0
8
  app_file: app.py
9
  pinned: true
10
+ ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -1,16 +1,14 @@
1
  import os
2
  import gradio as gr
3
  from hivision import IDCreator
4
- from hivision.error import FaceError
5
  from hivision.utils import add_background, resize_image_to_kb
6
  from hivision.creator.layout_calculator import (
7
  generate_layout_photo,
8
  generate_layout_image,
9
  )
10
- from hivision.creator.human_matting import (
11
- extract_human_modnet_photographic_portrait_matting,
12
- extract_human,
13
- )
14
  import pathlib
15
  import numpy as np
16
  from demo.utils import csv_to_size_list
@@ -59,6 +57,7 @@ def idphoto_inference(
59
  custom_image_kb,
60
  language,
61
  matting_model_option,
 
62
  head_measure_ratio=0.2,
63
  head_height_ratio=0.45,
64
  top_distance_max=0.12,
@@ -78,7 +77,7 @@ def idphoto_inference(
78
  "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800.": "宽度应不大于长度;长宽不应小于 100,大于 1800",
79
  "Custom Color": "自定义底色",
80
  "Custom": "自定义",
81
- "The number of faces is not equal to 1": "人脸数量不等于 1",
82
  "Solid Color": "纯色",
83
  "Up-Down Gradient (White)": "上下渐变 (白)",
84
  "Center Gradient (White)": "中心渐变 (白)",
@@ -92,7 +91,7 @@ def idphoto_inference(
92
  "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800.": "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800.",
93
  "Custom Color": "Custom Color",
94
  "Custom": "Custom",
95
- "The number of faces is not equal to 1": "The number of faces is not equal to 1",
96
  "Solid Color": "Solid Color",
97
  "Up-Down Gradient (White)": "Up-Down Gradient (White)",
98
  "Center Gradient (White)": "Center Gradient (White)",
@@ -156,6 +155,11 @@ def idphoto_inference(
156
  else:
157
  creator.matting_handler = extract_human
158
 
 
 
 
 
 
159
  change_bg_only = idphoto_json["size_mode"] in ["只换底", "Only Change Background"]
160
  # 生成证件照
161
  try:
@@ -170,8 +174,21 @@ def idphoto_inference(
170
  result_message = {
171
  img_output_standard: gr.update(value=None),
172
  img_output_standard_hd: gr.update(value=None),
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  notification: gr.update(
174
- value=text_lang_map[language]["The number of faces is not equal to 1"],
175
  visible=True,
176
  ),
177
  }
@@ -284,6 +301,12 @@ if __name__ == "__main__":
284
  # argparser.add_argument(
285
  # "--host", type=str, default="127.0.0.1", help="The host of the server"
286
  # )
 
 
 
 
 
 
287
 
288
  # args = argparser.parse_args()
289
 
@@ -294,6 +317,12 @@ if __name__ == "__main__":
294
  for file in os.listdir(os.path.join(root_dir, "hivision/creator/weights"))
295
  if file.endswith(".onnx")
296
  ]
 
 
 
 
 
 
297
 
298
  size_mode_CN = ["尺寸列表", "只换底", "自定义尺寸"]
299
  size_mode_EN = ["Size List", "Only Change Background", "Custom Size"]
@@ -353,10 +382,16 @@ if __name__ == "__main__":
353
  value="中文",
354
  elem_id="language",
355
  )
 
 
 
 
 
 
356
  matting_model_options = gr.Dropdown(
357
  choices=matting_model_list,
358
- label="Matting Model",
359
- value="modnet_photographic_portrait_matting",
360
  elem_id="matting_model",
361
  )
362
 
@@ -482,6 +517,8 @@ if __name__ == "__main__":
482
  choices=image_kb_CN,
483
  value="不设置",
484
  ),
 
 
485
  custom_image_kb_size: gr.update(label="KB 大小"),
486
  notification: gr.update(label="状态"),
487
  img_output_standard: gr.update(label="标准照"),
@@ -518,6 +555,8 @@ if __name__ == "__main__":
518
  choices=image_kb_EN,
519
  value="Not Set",
520
  ),
 
 
521
  custom_image_kb_size: gr.update(label="KB size"),
522
  notification: gr.update(label="Status"),
523
  img_output_standard: gr.update(label="Standard photo"),
@@ -574,6 +613,8 @@ if __name__ == "__main__":
574
  img_but,
575
  render_options,
576
  image_kb_options,
 
 
577
  custom_image_kb_size,
578
  notification,
579
  img_output_standard,
@@ -614,6 +655,7 @@ if __name__ == "__main__":
614
  custom_image_kb_size,
615
  language_options,
616
  matting_model_options,
 
617
  ],
618
  outputs=[
619
  img_output_standard,
@@ -624,4 +666,10 @@ if __name__ == "__main__":
624
  ],
625
  )
626
 
627
- demo.launch()
 
 
 
 
 
 
 
1
  import os
2
  import gradio as gr
3
  from hivision import IDCreator
4
+ from hivision.error import FaceError, APIError
5
  from hivision.utils import add_background, resize_image_to_kb
6
  from hivision.creator.layout_calculator import (
7
  generate_layout_photo,
8
  generate_layout_image,
9
  )
10
+ from hivision.creator.human_matting import *
11
+ from hivision.creator.face_detector import *
 
 
12
  import pathlib
13
  import numpy as np
14
  from demo.utils import csv_to_size_list
 
57
  custom_image_kb,
58
  language,
59
  matting_model_option,
60
+ face_detect_option,
61
  head_measure_ratio=0.2,
62
  head_height_ratio=0.45,
63
  top_distance_max=0.12,
 
77
  "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800.": "宽度应不大于长度;长宽不应小于 100,大于 1800",
78
  "Custom Color": "自定义底色",
79
  "Custom": "自定义",
80
+ "The number of faces is not equal to 1, please upload an image with a single face. If the actual number of faces is 1, it may be an issue with the accuracy of the detection model. Please switch to a different face detection model on the left or raise a Github Issue to notify the author.": "人脸数量不等于 1,请上传单张人脸的图像。如果实际人脸数量为 1,可能是检测模型精度的问题,请在左边更换人脸检测模型或给作者提Github Issue。",
81
  "Solid Color": "纯色",
82
  "Up-Down Gradient (White)": "上下渐变 (白)",
83
  "Center Gradient (White)": "中心渐变 (白)",
 
91
  "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800.": "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800.",
92
  "Custom Color": "Custom Color",
93
  "Custom": "Custom",
94
+ "The number of faces is not equal to 1, please upload an image with a single face. If the actual number of faces is 1, it may be an issue with the accuracy of the detection model. Please switch to a different face detection model on the left or raise a Github Issue to notify the author.": "The number of faces is not equal to 1, please upload an image with a single face. If the actual number of faces is 1, it may be an issue with the accuracy of the detection model. Please switch to a different face detection model on the left or raise a Github Issue to notify the author.",
95
  "Solid Color": "Solid Color",
96
  "Up-Down Gradient (White)": "Up-Down Gradient (White)",
97
  "Center Gradient (White)": "Center Gradient (White)",
 
155
  else:
156
  creator.matting_handler = extract_human
157
 
158
+ if face_detect_option == "mtcnn":
159
+ creator.detection_handler = detect_face_mtcnn
160
+ else:
161
+ creator.detection_handler = detect_face_face_plusplus
162
+
163
  change_bg_only = idphoto_json["size_mode"] in ["只换底", "Only Change Background"]
164
  # 生成证件照
165
  try:
 
174
  result_message = {
175
  img_output_standard: gr.update(value=None),
176
  img_output_standard_hd: gr.update(value=None),
177
+ img_output_layout: gr.update(visible=False),
178
+ notification: gr.update(
179
+ value=text_lang_map[language][
180
+ "The number of faces is not equal to 1, please upload an image with a single face. If the actual number of faces is 1, it may be an issue with the accuracy of the detection model. Please switch to a different face detection model on the left or raise a Github Issue to notify the author."
181
+ ],
182
+ visible=True,
183
+ ),
184
+ }
185
+ except APIError as e:
186
+ result_message = {
187
+ img_output_standard: gr.update(value=None),
188
+ img_output_standard_hd: gr.update(value=None),
189
+ img_output_layout: gr.update(visible=False),
190
  notification: gr.update(
191
+ value=f"Please make sure you have correctly set up the Face++ API Key and Secret.\nAPI Error\nStatus Code is {e.status_code}\nPossible errors are: {e}\n",
192
  visible=True,
193
  ),
194
  }
 
301
  # argparser.add_argument(
302
  # "--host", type=str, default="127.0.0.1", help="The host of the server"
303
  # )
304
+ # argparser.add_argument(
305
+ # "--root_path",
306
+ # type=str,
307
+ # default=None,
308
+ # help="The root path of the server, default is None (='/'), e.g. '/myapp'",
309
+ # )
310
 
311
  # args = argparser.parse_args()
312
 
 
317
  for file in os.listdir(os.path.join(root_dir, "hivision/creator/weights"))
318
  if file.endswith(".onnx")
319
  ]
320
+ DEFAULT_MATTING_MODEL = "modnet_photographic_portrait_matting"
321
+ if DEFAULT_MATTING_MODEL in matting_model_list:
322
+ matting_model_list.remove(DEFAULT_MATTING_MODEL)
323
+ matting_model_list.insert(0, DEFAULT_MATTING_MODEL)
324
+
325
+ face_detect_model_list = ["mtcnn", "face++ (联网API)"]
326
 
327
  size_mode_CN = ["尺寸列表", "只换底", "自定义尺寸"]
328
  size_mode_EN = ["Size List", "Only Change Background", "Custom Size"]
 
382
  value="中文",
383
  elem_id="language",
384
  )
385
+ face_detect_model_options = gr.Dropdown(
386
+ choices=face_detect_model_list,
387
+ label="人脸检测模型",
388
+ value=face_detect_model_list[0],
389
+ elem_id="matting_model",
390
+ )
391
  matting_model_options = gr.Dropdown(
392
  choices=matting_model_list,
393
+ label="抠图模型",
394
+ value=matting_model_list[0],
395
  elem_id="matting_model",
396
  )
397
 
 
517
  choices=image_kb_CN,
518
  value="不设置",
519
  ),
520
+ matting_model_options: gr.update(label="抠图模型"),
521
+ face_detect_model_options: gr.update(label="人脸检测模型"),
522
  custom_image_kb_size: gr.update(label="KB 大小"),
523
  notification: gr.update(label="状态"),
524
  img_output_standard: gr.update(label="标准照"),
 
555
  choices=image_kb_EN,
556
  value="Not Set",
557
  ),
558
+ matting_model_options: gr.update(label="Matting model"),
559
+ face_detect_model_options: gr.update(label="Face detect model"),
560
  custom_image_kb_size: gr.update(label="KB size"),
561
  notification: gr.update(label="Status"),
562
  img_output_standard: gr.update(label="Standard photo"),
 
613
  img_but,
614
  render_options,
615
  image_kb_options,
616
+ matting_model_options,
617
+ face_detect_model_options,
618
  custom_image_kb_size,
619
  notification,
620
  img_output_standard,
 
655
  custom_image_kb_size,
656
  language_options,
657
  matting_model_options,
658
+ face_detect_model_options,
659
  ],
660
  outputs=[
661
  img_output_standard,
 
666
  ],
667
  )
668
 
669
+ demo.launch(
670
+ # server_name=args.host,
671
+ # server_port=args.port,
672
+ # show_api=False,
673
+ # favicon_path=os.path.join(root_dir, "assets/hivision_logo.png"),
674
+ # root_path=args.root_path,
675
+ )
app.spec DELETED
@@ -1,44 +0,0 @@
1
- # -*- mode: python ; coding: utf-8 -*-
2
- from PyInstaller.utils.hooks import collect_data_files
3
-
4
- datas = [('hivisionai', 'hivisionai'), ('hivision_modnet.onnx', '.'), ('size_list_CN.csv', '.')]
5
- datas += collect_data_files('gradio_client')
6
- datas += collect_data_files('gradio')
7
-
8
-
9
- a = Analysis(
10
- ['app/web.py'],
11
- pathex=[],
12
- binaries=[],
13
- datas=datas,
14
- hiddenimports=[],
15
- hookspath=[],
16
- hooksconfig={},
17
- runtime_hooks=[],
18
- excludes=[],
19
- noarchive=False,
20
- optimize=0,
21
- )
22
- pyz = PYZ(a.pure)
23
-
24
- exe = EXE(
25
- pyz,
26
- a.scripts,
27
- a.binaries,
28
- a.datas,
29
- [],
30
- name='HivisionIDPhotos',
31
- debug=False,
32
- bootloader_ignore_signals=False,
33
- strip=False,
34
- upx=True,
35
- upx_exclude=[],
36
- runtime_tmpdir=None,
37
- console=True,
38
- disable_windowed_traceback=False,
39
- argv_emulation=False,
40
- target_arch=None,
41
- codesign_identity=None,
42
- entitlements_file=None,
43
- icon=['assets\hivisionai.ico'],
44
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/demoImage.png DELETED

Git LFS Details

  • SHA256: 18aba5dc15d286156cd06c3c977f3e029e94e8fd48afb9b53264048cc2a4d429
  • Pointer size: 132 Bytes
  • Size of remote file: 1.62 MB
assets/gradio-image.jpeg DELETED
Binary file (395 kB)
 
assets/hivisionai.ico DELETED
Binary file (21.7 kB)
 
deploy_api.py DELETED
@@ -1,157 +0,0 @@
1
- from fastapi import FastAPI, UploadFile, Form
2
- import onnxruntime
3
- from hivision import IDCreator
4
- from hivision.error import FaceError
5
- from hivision.creator.layout_calculator import (
6
- generate_layout_photo,
7
- generate_layout_image,
8
- )
9
- from hivision.utils import add_background, resize_image_to_kb_base64, hex_to_rgb
10
- import base64
11
- import numpy as np
12
- import cv2
13
- import os
14
-
15
- app = FastAPI()
16
- creator = IDCreator()
17
-
18
-
19
- # 将图像转换为Base64编码
20
- def numpy_2_base64(img: np.ndarray):
21
- retval, buffer = cv2.imencode(".png", img)
22
- base64_image = base64.b64encode(buffer).decode("utf-8")
23
-
24
- return base64_image
25
-
26
-
27
- # 证件照智能制作接口
28
- @app.post("/idphoto")
29
- async def idphoto_inference(
30
- input_image: UploadFile,
31
- height: str = Form(...),
32
- width: str = Form(...),
33
- head_measure_ratio=0.2,
34
- head_height_ratio=0.45,
35
- top_distance_max=0.12,
36
- top_distance_min=0.10,
37
- ):
38
-
39
- image_bytes = await input_image.read()
40
- nparr = np.frombuffer(image_bytes, np.uint8)
41
- img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
42
-
43
- # 将字符串转为元组
44
- size = (int(height), int(width))
45
- try:
46
- result = creator(
47
- img,
48
- size=size,
49
- head_measure_ratio=head_measure_ratio,
50
- head_height_ratio=head_height_ratio,
51
- )
52
- except FaceError:
53
- result_message = {"status": False}
54
- # 如果检测到人脸数量等于1, 则返回标准证和高清照结果(png 4通道图像)
55
- else:
56
- result_message = {
57
- "status": True,
58
- "image_base64_standard": numpy_2_base64(result.standard),
59
- "image_base64_hd": numpy_2_base64(result.hd),
60
- }
61
-
62
- return result_message
63
-
64
-
65
- # 透明图像添加纯色背景接口
66
- @app.post("/add_background")
67
- async def photo_add_background(
68
- input_image: UploadFile, color: str = Form(...), kb: str = Form(None)
69
- ):
70
- image_bytes = await input_image.read()
71
- nparr = np.frombuffer(image_bytes, np.uint8)
72
- img = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED)
73
-
74
- color = hex_to_rgb(color)
75
- color = (color[2], color[1], color[0])
76
-
77
- result_image = add_background(img, bgr=color).astype(np.uint8)
78
-
79
- if kb:
80
- result_image = cv2.cvtColor(result_image, cv2.COLOR_RGB2BGR)
81
- result_image_base64 = resize_image_to_kb_base64(result_image, int(kb))
82
- else:
83
- result_image_base64 = numpy_2_base64(result_image)
84
-
85
- # try:
86
-
87
- result_messgae = {
88
- "status": True,
89
- "image_base64": result_image_base64,
90
- }
91
-
92
- # except Exception as e:
93
- # print(e)
94
- # result_messgae = {
95
- # "status": False,
96
- # "error": e
97
- # }
98
-
99
- return result_messgae
100
-
101
-
102
- # 六寸排版照生成接口
103
- @app.post("/generate_layout_photos")
104
- async def generate_layout_photos(
105
- input_image: UploadFile,
106
- height: str = Form(...),
107
- width: str = Form(...),
108
- kb: str = Form(None),
109
- ):
110
- # try:
111
- image_bytes = await input_image.read()
112
- nparr = np.frombuffer(image_bytes, np.uint8)
113
- img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
114
-
115
- size = (int(height), int(width))
116
-
117
- typography_arr, typography_rotate = generate_layout_photo(
118
- input_height=size[0], input_width=size[1]
119
- )
120
-
121
- result_layout_image = generate_layout_image(
122
- img, typography_arr, typography_rotate, height=size[0], width=size[1]
123
- ).astype(np.uint8)
124
-
125
- if kb:
126
- result_layout_image = cv2.cvtColor(result_layout_image, cv2.COLOR_RGB2BGR)
127
- result_layout_image_base64 = resize_image_to_kb_base64(
128
- result_layout_image, int(kb)
129
- )
130
- else:
131
- result_layout_image_base64 = numpy_2_base64(result_layout_image)
132
-
133
- result_messgae = {
134
- "status": True,
135
- "image_base64": result_layout_image_base64,
136
- }
137
-
138
- # except Exception as e:
139
- # result_messgae = {
140
- # "status": False,
141
- # }
142
-
143
- return result_messgae
144
-
145
-
146
- if __name__ == "__main__":
147
- import uvicorn
148
-
149
- # 加载权重文件
150
- root_dir = os.path.dirname(os.path.abspath(__file__))
151
- HY_HUMAN_MATTING_WEIGHTS_PATH = os.path.join(
152
- root_dir, "hivision/creator/weights/hivision_modnet.onnx"
153
- )
154
- sess = onnxruntime.InferenceSession(HY_HUMAN_MATTING_WEIGHTS_PATH)
155
-
156
- # 在8080端口运行推理服务
157
- uvicorn.run(app, host="0.0.0.0", port=8080)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docker-compose.yml DELETED
@@ -1,11 +0,0 @@
1
- version: "3.8"
2
-
3
- services:
4
- hivision_idphotos:
5
- build:
6
- context: .
7
- dockerfile: Dockerfile
8
- image: hivision_idphotos2
9
- command: python3 app.py --host 0.0.0.0 --port 7860
10
- ports:
11
- - "7860:7860"
 
 
 
 
 
 
 
 
 
 
 
 
docs/api_CN.md DELETED
@@ -1,554 +0,0 @@
1
- # API Docs
2
-
3
- 中文 | [English](api_EN.md)
4
-
5
- ## 目录
6
-
7
- - [开始之前:开启后端服务](#开始之前开启后端服务)
8
- - [接口功能说明](#接口功能说明)
9
- - [cURL 请求示例](#curl-请求示例)
10
- - [Python 请求示例](#python-请求示例)
11
- - [Python Requests 请求方法](#1️⃣-python-requests-请求方法)
12
- - [Python 脚本请求方法](#2️⃣-python-脚本请求方法)
13
- - [Java 请求示例](#java-请求示例)
14
- - [Javascript 请求示例](#javascript-请求示例)
15
-
16
- ## 开始之前:开启后端服务
17
-
18
- 在请求 API 之前,请先运行后端服务
19
-
20
- ```bash
21
- python delopy_api.py
22
- ```
23
-
24
- <br>
25
-
26
- ## 接口功能说明
27
-
28
- ### 1.生成证件照(底透明)
29
-
30
- 接口名:`idphoto`
31
-
32
- `生成证件照`接口的逻辑是发送一张 RGB 图像,输出一张标准证件照和一张高清证件照:
33
-
34
- - **高清证件照**:根据`size`的宽高比例制作的证件照,文件名为`output_image_dir`增加`_hd`后缀
35
- - **标准证件照**:尺寸等于`size`,由高清证件照缩放而来,文件名为`output_image_dir`
36
-
37
- 需要注意的是,生成的两张照片都是透明的(RGBA 四通道图像),要生成完整的证件照,还需要下面的`添加背景色`接口。
38
-
39
- > 问:为什么这么设计?
40
- > 答:因为在实际产品中,经常用户会频繁切换底色预览效果,直接给透明底图像,由前端 js 代码合成颜色是更好体验的做法。
41
-
42
- ### 2.添加背景色
43
-
44
- 接口名:`add_background`
45
-
46
- `添加背景色`接口的逻辑是发送一张 RGBA 图像,根据`color`添加背景色,合成一张 JPG 图像。
47
-
48
- ### 3.生成六寸排版照
49
-
50
- 接口名:`generate_layout_photos`
51
-
52
- `生成六寸排版照`接口的逻辑是发送一张 RGB 图像(一般为添加背景色之后的证件照),根据`size`进行照片排布,然后生成一张六寸排版照。
53
-
54
- <br>
55
-
56
-
57
- ## cURL 请求示例
58
-
59
- cURL 是一个命令行工具,用于使用各种网络协议传输数据。以下是使用 cURL 调用这些 API 的示例。
60
-
61
- ### 1. 生成证件照(底透明)
62
-
63
- ```bash
64
- curl -X POST "http://127.0.0.1:8080/idphoto" \
65
- -F "input_image=@demo/images/test.jpg" \
66
- -F "height=413" \
67
- -F "width=295"
68
- ```
69
-
70
- ### 2. 添加背景色
71
-
72
- ```bash
73
- curl -X POST "http://127.0.0.1:8080/add_background" \
74
75
- -F "color=638cce" \
76
- -F "kb=200"
77
- ```
78
-
79
- ### 3. 生成六寸排版照
80
-
81
- ```bash
82
- curl -X POST "http://127.0.0.1:8080/generate_layout_photos" \
83
84
- -F "height=413" \
85
- -F "width=295" \
86
- -F "kb=200"
87
- ```
88
-
89
-
90
- ## Python 请求示例
91
-
92
- ### 1️⃣ Python Requests 请求方法
93
-
94
- #### 1.生成证件照(底透明)
95
-
96
- ```python
97
- import requests
98
-
99
- url = "http://127.0.0.1:8080/idphoto"
100
- input_image_path = "images/test.jpg"
101
-
102
- files = {"input_image": open(input_image_path, "rb")}
103
- data = {"height": 413, "width": 295}
104
-
105
- response = requests.post(url, files=files, data=data).json()
106
-
107
- # response为一个json格式字典,包含status、image_base64_standard和image_base64_hd三项
108
- print(response)
109
-
110
- ```
111
-
112
- #### 2.添加背景色
113
-
114
- ```python
115
- import requests
116
-
117
- url = "http://127.0.0.1:8080/add_background"
118
- input_image_path = "test.png"
119
-
120
- files = {"input_image": open(input_image_path, "rb")}
121
- data = {"color": '638cce', 'kb': None}
122
-
123
- response = requests.post(url, files=files, data=data).json()
124
-
125
- # response为一个json格式字典,包含status和image_base64
126
- print(response)
127
- ```
128
-
129
- #### 3.生成六寸排版照
130
-
131
- ```python
132
- import requests
133
-
134
- url = "http://127.0.0.1:8080/generate_layout_photos"
135
- input_image_path = "test.jpg"
136
-
137
- files = {"input_image": open(input_image_path, "rb")}
138
- data = {"height": 413, "width": 295, "kb": 200}
139
-
140
- response = requests.post(url, files=files, data=data).json()
141
-
142
- # response为一个json格式字典,包含status和image_base64
143
- print(response)
144
- ```
145
-
146
- <br>
147
-
148
- ### 2️⃣ Python 脚本请求方法
149
-
150
- ```bash
151
- python requests_api.py -u <URL> -t <TYPE> -i <INPUT_IMAGE_DIR> -o <OUTPUT_IMAGE_DIR> [--height <HEIGHT>] [--width <WIDTH>] [-c <COLOR>] [-k <KB>]
152
- ```
153
-
154
- #### 参数说明
155
-
156
- ##### 基本参数
157
-
158
- - `-u`, `--url`
159
-
160
- - **描述**: API 服务的 URL。
161
- - **默认值**: `http://127.0.0.1:8080`
162
-
163
- - `-t`, `--type`
164
-
165
- - **描述**: 请求 API 的种类,可选值有 `idphoto`、`add_background` 和 `generate_layout_photos`。分别代表证件照制作、透明图加背景和排版照生成。
166
- - **默认值**: `idphoto`
167
-
168
- - `-i`, `--input_image_dir`
169
-
170
- - **描述**: 输入图像路径。
171
- - **必需**: 是
172
- - **示例**: `./input_images/photo.jpg`
173
-
174
- - `-o`, `--output_image_dir`
175
- - **描述**: 保存图像路径。
176
- - **必需**: 是
177
- - **示例**: `./output_images/processed_photo.jpg`
178
-
179
- ##### 可选参数
180
-
181
- - `--height`,
182
- - **描述**: 标准证件照的输出尺寸的高度。
183
- - **默认值**: 413
184
- - `--width`,
185
-
186
- - **描述**: 标准证件照的输出尺寸的宽度。
187
- - **默认值**: 295
188
-
189
- - `-c`, `--color`
190
-
191
- - **描述**: 给透明图增加背景色,格式为 Hex(如#638cce),仅在 type 为`add_background`时生效
192
- - **默认值**: `638cce`
193
-
194
- - `-k`, `--kb`
195
- - **描述**: 输出照片的 KB 值,仅在 type 为`add_background`和`generate_layout_photos`时生效,值为 None 时不做设置。
196
- - **默认值**: `None`
197
- - **示例**: `50`
198
-
199
- ### 1.生成证件照(底透明)
200
-
201
- ```bash
202
- python requests_api.py \
203
- -u http://127.0.0.1:8080 \
204
- -t idphoto \
205
- -i ./photo.jpg \
206
- -o ./idphoto.png \
207
- --height 413 \
208
- --width 295
209
- ```
210
-
211
- ### 2.添加背景色
212
-
213
- ```bash
214
- python requests_api.py \
215
- -u http://127.0.0.1:8080 \
216
- -t add_background \
217
- -i ./idphoto.png \
218
- -o ./idphoto_with_background.jpg \
219
- -c 638cce \
220
- -k 50
221
- ```
222
-
223
- ### 3.生成六寸排版照
224
-
225
- ```bash
226
- python requests_api.py \
227
- -u http://127.0.0.1:8080 \
228
- -t generate_layout_photos \
229
- -i ./idphoto_with_background.jpg \
230
- -o ./layout_photo.jpg \
231
- --height 413 \
232
- --width 295 \
233
- -k 200
234
- ```
235
-
236
- ### 请求失败的情况
237
-
238
- - 照片中检测到的人脸大于 1,则失败
239
-
240
- ## Java 请求示例
241
-
242
- ### 添加 maven 依赖
243
-
244
- ```java
245
- <dependency>
246
- <groupId>cn.hutool</groupId>
247
- <artifactId>hutool-all</artifactId>
248
- <version>5.8.16</version>
249
- </dependency>
250
-
251
- <dependency>
252
- <groupId>commons-io</groupId>
253
- <artifactId>commons-io</artifactId>
254
- <version>2.6</version>
255
- </dependency>
256
- ```
257
-
258
- ### 运行代码
259
-
260
- #### 1.生成证件照(底透明)
261
-
262
- ```java
263
- /**
264
- * 生成证件照(底透明) /idphoto 接口
265
- * @param inputImageDir 文件地址
266
- * @return
267
- * @throws IOException
268
- */
269
- public static String requestIdPhoto(String inputImageDir) throws IOException {
270
- String url = BASE_URL+"/idphoto";
271
- // 创建文件对象
272
- File inputFile = new File(inputImageDir);
273
- Map<String, Object> paramMap=new HashMap<>();
274
- paramMap.put("input_image",inputFile);
275
- paramMap.put("height","413");
276
- paramMap.put("width","295");
277
- //包含status、image_base64_standard和image_base64_hd三项
278
- return HttpUtil.post(url, paramMap);
279
- }
280
- ```
281
-
282
- #### 2.添加背景色
283
-
284
- ```java
285
- /**
286
- * 添加背景色 /add_background 接口
287
- * @param inputImageDir 文件地址
288
- * @return
289
- * @throws IOException
290
- */
291
- public static String requestAddBackground(String inputImageDir) throws IOException {
292
- String url = BASE_URL+"/add_background";
293
- // 创建文件对象
294
- File inputFile = new File(inputImageDir);
295
- Map<String, Object> paramMap=new HashMap<>();
296
- paramMap.put("input_image",inputFile);
297
- paramMap.put("color","638cce");
298
- paramMap.put("kb","200");
299
- // response为一个json格式字典,包含status和image_base64
300
- return HttpUtil.post(url, paramMap);
301
- }
302
- ```
303
-
304
- #### 3.生成六寸排版照
305
-
306
- ```java
307
- /**
308
- * 生成六寸排版照 /generate_layout_photos 接口
309
- * @param inputImageDir 文件地址
310
- * @return
311
- * @throws IOException
312
- */
313
- public static String requestGenerateLayoutPhotos(String inputImageDir) throws IOException {
314
- String url = BASE_URL+"/generate_layout_photos";
315
- // 创建文件对象
316
- File inputFile = new File(inputImageDir);
317
- Map<String, Object> paramMap=new HashMap<>();
318
- paramMap.put("input_image",inputFile);
319
- paramMap.put("height","413");
320
- paramMap.put("width","295");
321
- paramMap.put("kb","200");
322
- //response为一个json格式字典,包含status和image_base64
323
- return HttpUtil.post(url, paramMap);
324
- }
325
- ```
326
-
327
- #### 4.汇总
328
-
329
- ```java
330
-
331
- import cn.hutool.http.HttpUtil;
332
- import cn.hutool.json.JSONObject;
333
- import cn.hutool.json.JSONUtil;
334
- import org.apache.commons.io.FileUtils;
335
- import org.springframework.util.StringUtils;
336
- import java.io.File;
337
- import java.io.IOException;
338
- import java.util.Base64;
339
- import java.util.HashMap;
340
- import java.util.Map;
341
-
342
- /**
343
- * @author: qingshuang
344
- * @createDate: 2024/09/05
345
- * @description: java生成证件照,测试用例
346
- */
347
- public class Test {
348
- /**
349
- * 接口地址
350
- */
351
- private final static String BASE_URL = "http://127.0.0.1:8080";
352
-
353
- /**
354
- * 生成证件照(底透明) /idphoto 接口
355
- * @param inputImageDir 文件地址
356
- * @return
357
- * @throws IOException
358
- */
359
- public static String requestIdPhoto(String inputImageDir) throws IOException {
360
- String url = BASE_URL+"/idphoto";
361
- // 创建文件对象
362
- File inputFile = new File(inputImageDir);
363
- Map<String, Object> paramMap=new HashMap<>();
364
- paramMap.put("input_image",inputFile);
365
- paramMap.put("height","413");
366
- paramMap.put("width","295");
367
- return HttpUtil.post(url, paramMap);
368
- }
369
- /**
370
- * 添加背景色 /add_background 接口
371
- * @param inputImageDir 文件地址
372
- * @return
373
- * @throws IOException
374
- */
375
- public static String requestAddBackground(String inputImageDir) throws IOException {
376
- String url = BASE_URL+"/add_background";
377
- // 创建文件对象
378
- File inputFile = new File(inputImageDir);
379
- Map<String, Object> paramMap=new HashMap<>();
380
- paramMap.put("input_image",inputFile);
381
- paramMap.put("color","638cce");
382
- paramMap.put("kb","200");
383
- return HttpUtil.post(url, paramMap);
384
- }
385
- /**
386
- * 生成六寸排版照 /generate_layout_photos 接口
387
- * @param inputImageDir 文件地址
388
- * @return
389
- * @throws IOException
390
- */
391
- public static String requestGenerateLayoutPhotos(String inputImageDir) throws IOException {
392
- String url = BASE_URL+"/generate_layout_photos";
393
- // 创建文件对象
394
- File inputFile = new File(inputImageDir);
395
- Map<String, Object> paramMap=new HashMap<>();
396
- paramMap.put("input_image",inputFile);
397
- paramMap.put("height","413");
398
- paramMap.put("width","295");
399
- paramMap.put("kb","200");
400
- return HttpUtil.post(url, paramMap);
401
- }
402
- /**
403
- * 生成证件照(底透明)
404
- * @param inputImageDir 源文件地址
405
- * @param outputImageDir 输出文件地址
406
- * @throws IOException
407
- */
408
- private static void requestIdPhotoToImage(String inputImageDir, String outputImageDir) throws IOException {
409
- String res =requestIdPhoto(inputImageDir);
410
- //转成json
411
- JSONObject response= JSONUtil.parseObj(res);
412
- if(response.getBool("status")){//请求接口成功
413
- String image_base64_standard= response.getStr("image_base64_standard");
414
- String image_base64_hd =response.getStr("image_base64_hd");
415
- String[] outputImageDirArr= StringUtils.split(outputImageDir,".");
416
- // Base64 保存为图片
417
- FileUtils.writeByteArrayToFile(new File(outputImageDirArr[0]+"_standard."+outputImageDirArr[1]), Base64.getDecoder().decode(image_base64_standard));
418
- FileUtils.writeByteArrayToFile(new File(outputImageDirArr[0]+"_hd."+outputImageDirArr[1]), Base64.getDecoder().decode(image_base64_hd));
419
- }
420
- }
421
- /**
422
- * 添加背景色
423
- * @param inputImageDir 源文件地址
424
- * @param outputImageDir 输出文件地址
425
- * @throws IOException
426
- */
427
- private static void requestAddBackgroundToImage(String inputImageDir, String outputImageDir) throws IOException {
428
- String res =requestAddBackground(inputImageDir);
429
- //转成json
430
- JSONObject response= JSONUtil.parseObj(res);
431
- if(response.getBool("status")){//请求接口成功
432
- String image_base64= response.getStr("image_base64");
433
- String[] outputImageDirArr= StringUtils.split(outputImageDir,".");
434
- // Base64 保存为图片
435
- FileUtils.writeByteArrayToFile(new File(outputImageDirArr[0]+"_background."+outputImageDirArr[1]), Base64.getDecoder().decode(image_base64));
436
- }
437
- }
438
- /**
439
- * 生成六寸排版照
440
- * @param inputImageDir 源文件地址
441
- * @param outputImageDir 输出文件地址
442
- * @throws IOException
443
- */
444
- private static void requestGenerateLayoutPhotosToImage(String inputImageDir, String outputImageDir) throws IOException {
445
- String res =requestGenerateLayoutPhotos(inputImageDir);
446
- //转成json
447
- JSONObject response= JSONUtil.parseObj(res);
448
- if(response.getBool("status")){//请求接口成功
449
- String image_base64= response.getStr("image_base64");
450
- String[] outputImageDirArr= StringUtils.split(outputImageDir,".");
451
- // Base64 保存为图片
452
- FileUtils.writeByteArrayToFile(new File(outputImageDirArr[0]+"_layout."+outputImageDirArr[1]), Base64.getDecoder().decode(image_base64));
453
- }
454
- }
455
-
456
- public static void main(String[] args) {
457
- try {
458
- //生成证件照(底透明)
459
- requestIdPhotoToImage("C:\\Users\\Administrator\\Desktop\\1111.jpg","C:\\Users\\Administrator\\Desktop\\2222.png");
460
- //添加背景色
461
- requestAddBackgroundToImage("C:\\Users\\Administrator\\Desktop\\2222_hd.png","C:\\Users\\Administrator\\Desktop\\idphoto_with_background.jpg");
462
- //生成六寸排版照
463
- requestGenerateLayoutPhotosToImage("C:\\Users\\Administrator\\Desktop\\1111.jpg","C:\\Users\\Administrator\\Desktop\\2222.png");
464
-
465
- } catch (IOException e) {
466
- e.printStackTrace();
467
- }
468
- }
469
- }
470
-
471
- ```
472
-
473
- ## JavaScript 请求示例
474
-
475
- 在JavaScript中,我们可以使用`fetch` API来发送HTTP请求。以下是如何使用JavaScript调用这些API的示例。
476
-
477
- ### 1. 生成证件照(底透明)
478
-
479
- ```javascript
480
- async function generateIdPhoto(inputImagePath, height, width) {
481
- const url = "http://127.0.0.1:8080/idphoto";
482
- const formData = new FormData();
483
- formData.append("input_image", new File([await fetch(inputImagePath).then(res => res.blob())], "test.jpg"));
484
- formData.append("height", height);
485
- formData.append("width", width);
486
-
487
- const response = await fetch(url, {
488
- method: 'POST',
489
- body: formData
490
- });
491
-
492
- const result = await response.json();
493
- console.log(result);
494
- return result;
495
- }
496
-
497
- // 示例调用
498
- generateIdPhoto("images/test.jpg", 413, 295).then(response => {
499
- console.log(response);
500
- });
501
- ```
502
-
503
- ### 2. 添加背景色
504
-
505
- ```javascript
506
- async function addBackground(inputImagePath, color, kb) {
507
- const url = "http://127.0.0.1:8080/add_background";
508
- const formData = new FormData();
509
- formData.append("input_image", new File([await fetch(inputImagePath).then(res => res.blob())], "test.png"));
510
- formData.append("color", color);
511
- formData.append("kb", kb);
512
-
513
- const response = await fetch(url, {
514
- method: 'POST',
515
- body: formData
516
- });
517
-
518
- const result = await response.json();
519
- console.log(result);
520
- return result;
521
- }
522
-
523
- // 示例调用
524
- addBackground("test.png", "638cce", 200).then(response => {
525
- console.log(response);
526
- });
527
- ```
528
-
529
- ### 3. 生成六寸排版照
530
-
531
- ```javascript
532
- async function generateLayoutPhotos(inputImagePath, height, width, kb) {
533
- const url = "http://127.0.0.1:8080/generate_layout_photos";
534
- const formData = new FormData();
535
- formData.append("input_image", new File([await fetch(inputImagePath).then(res => res.blob())], "test.jpg"));
536
- formData.append("height", height);
537
- formData.append("width", width);
538
- formData.append("kb", kb);
539
-
540
- const response = await fetch(url, {
541
- method: 'POST',
542
- body: formData
543
- });
544
-
545
- const result = await response.json();
546
- console.log(result);
547
- return result;
548
- }
549
-
550
- // 示例调用
551
- generateLayoutPhotos("test.jpg", 413, 295, 200).then(response => {
552
- console.log(response);
553
- });
554
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/api_EN.md DELETED
@@ -1,553 +0,0 @@
1
- # API Documentation
2
-
3
- [中文](api_CN.md) | English
4
-
5
- ## TOC
6
-
7
- - [Before You Start: Launch the Backend Service](#before-you-start-launch-the-backend-service)
8
- - [Interface Function Descriptions](#interface-function-descriptions)
9
- - [cURL Request Example](#curl-request-examples)
10
- - [Python Request Example](#python-request-example)
11
- - [Python Requests Method](#1️⃣-python-requests-method)
12
- - [Python Script Method](#2️⃣-python-script-request-method)
13
- - [Java Request Example](#java-request-example)
14
- - [Javascript Request Example](#javascript-request-examples)
15
-
16
- ## Before You Start: Launch the Backend Service
17
-
18
- Before making API requests, please run the backend service:
19
-
20
- ```bash
21
- python deploy_api.py
22
- ```
23
-
24
- <br>
25
-
26
- ## Interface Function Descriptions
27
-
28
- ### 1. Generate ID Photo (Transparent Background)
29
-
30
- Interface Name: `idphoto`
31
-
32
- The `Generate ID Photo` interface logic involves sending an RGB image and receiving a standard ID photo and a high-definition ID photo:
33
-
34
- - **High-Definition ID Photo**: An ID photo made according to the aspect ratio of `size`, with the filename being `output_image_dir` appended with `_hd` suffix.
35
- - **Standard ID Photo**: A photo with dimensions equal to `size`, scaled from the high-definition ID photo, with the filename being `output_image_dir`.
36
-
37
- It should be noted that both generated photos are transparent (RGBA four-channel images). To produce a complete ID photo, the following `Add Background Color` interface is also required.
38
-
39
- > Q: Why is this design used?
40
- > A: In actual products, users often need to frequently switch background colors to preview effects. Providing a transparent background image and allowing the front-end JavaScript code to synthesize the color offers a better user experience.
41
-
42
- ### 2. Add Background Color
43
-
44
- Interface Name: `add_background`
45
-
46
- The `Add Background Color` interface logic involves sending an RGBA image, adding a background color based on `color`, and synthesizing a JPG image.
47
-
48
- ### 3. Generate 6-inch Layout Photo
49
-
50
- Interface Name: `generate_layout_photos`
51
-
52
- The `Generate 6-inch Layout Photo` interface logic involves sending an RGB image (usually an ID photo after adding a background color), arranging the photos according to `size`, and then generating a 6-inch layout photo.
53
-
54
- <br>
55
-
56
-
57
- ## cURL Request Examples
58
-
59
- cURL is a command-line tool used to transfer data using various network protocols. Below are examples of how to use cURL to call these APIs.
60
-
61
- ### 1. Generate ID Photo (Transparent Background)
62
-
63
- ```bash
64
- curl -X POST "http://127.0.0.1:8080/idphoto" \
65
- -F "input_image=@demo/images/test.jpg" \
66
- -F "height=413" \
67
- -F "width=295"
68
- ```
69
-
70
- ### 2. Add Background Color
71
-
72
- ```bash
73
- curl -X POST "http://127.0.0.1:8080/add_background" \
74
75
- -F "color=638cce" \
76
- -F "kb=200"
77
- ```
78
-
79
- ### 3. Generate Six-Inch Layout Photo
80
-
81
- ```bash
82
- curl -X POST "http://127.0.0.1:8080/generate_layout_photos" \
83
84
- -F "height=413" \
85
- -F "width=295" \
86
- -F "kb=200"
87
- ```
88
-
89
- ## Python Request Example
90
-
91
- ### 1️⃣ Python Requests Method
92
-
93
- #### 1. Generate ID Photo (Transparent Background)
94
-
95
- ```python
96
- import requests
97
-
98
- url = "http://127.0.0.1:8080/idphoto"
99
- input_image_path = "images/test.jpg"
100
-
101
- files = {"input_image": open(input_image_path, "rb")}
102
- data = {"height": 413, "width": 295}
103
-
104
- response = requests.post(url, files=files, data=data).json()
105
-
106
- # response is a JSON dictionary containing status, image_base64_standard, and image_base64_hd
107
- print(response)
108
- ```
109
-
110
- #### 2. Add Background Color
111
-
112
- ```python
113
- import requests
114
-
115
- url = "http://127.0.0.1:8080/add_background"
116
- input_image_path = "test.png"
117
-
118
- files = {"input_image": open(input_image_path, "rb")}
119
- data = {"color": '638cce', 'kb': None}
120
-
121
- response = requests.post(url, files=files, data=data).json()
122
-
123
- # response is a JSON dictionary containing status and image_base64
124
- print(response)
125
- ```
126
-
127
- #### 3. Generate 6-inch Layout Photo
128
-
129
- ```python
130
- import requests
131
-
132
- url = "http://127.0.0.1:8080/generate_layout_photos"
133
- input_image_path = "test.jpg"
134
-
135
- files = {"input_image": open(input_image_path, "rb")}
136
- data = {"height": 413, "width": 295, "kb": 200}
137
-
138
- response = requests.post(url, files=files, data=data).json()
139
-
140
- # response is a JSON dictionary containing status and image_base64
141
- print(response)
142
- ```
143
-
144
- <br>
145
-
146
- ### 2️⃣ Python Script Request Method
147
-
148
- ```bash
149
- python requests_api.py -u <URL> -t <TYPE> -i <INPUT_IMAGE_DIR> -o <OUTPUT_IMAGE_DIR> [--height <HEIGHT>] [--width <WIDTH>] [-c <COLOR>] [-k <KB>]
150
- ```
151
-
152
- #### Parameter Descriptions
153
-
154
- ##### Basic Parameters
155
-
156
- - `-u`, `--url`
157
-
158
- - **Description**: The URL of the API service.
159
- - **Default Value**: `http://127.0.0.1:8080`
160
-
161
- - `-t`, `--type`
162
-
163
- - **Description**: The type of API request, with optional values being `idphoto`, `add_background`, and `generate_layout_photos`. They represent ID photo creation, transparent image background addition, and layout photo generation, respectively.
164
- - **Default Value**: `idphoto`
165
-
166
- - `-i`, `--input_image_dir`
167
-
168
- - **Description**: The path of the input image.
169
- - **Required**: Yes
170
- - **Example**: `./input_images/photo.jpg`
171
-
172
- - `-o`, `--output_image_dir`
173
- - **Description**: The path to save the image.
174
- - **Required**: Yes
175
- - **Example**: `./output_images/processed_photo.jpg`
176
-
177
- ##### Optional Parameters
178
-
179
- - `--height`
180
-
181
- - **Description**: The height of the output size for the standard ID photo.
182
- - **Default Value**: 413
183
-
184
- - `--width`
185
-
186
- - **Description**: The width of the output size for the standard ID photo.
187
- - **Default Value**: 295
188
-
189
- - `-c`, `--color`
190
-
191
- - **Description**: Adds a background color to the transparent image, in Hex format (e.g., #638cce), only effective when the type is `add_background`.
192
- - **Default Value**: `638cce`
193
-
194
- - `-k`, `--kb`
195
- - **Description**: The KB value of the output photo, only effective when the type is `add_background` or `generate_layout_photos`, and no setting is made when the value is None.
196
- - **Default Value**: `None`
197
- - **Example**: `50`
198
-
199
- #### 1. Generate ID Photo (Transparent Background)
200
-
201
- ```bash
202
- python requests_api.py \
203
- -u http://127.0.0.1:8080 \
204
- -t idphoto \
205
- -i ./photo.jpg \
206
- -o ./idphoto.png \
207
- --height 413 \
208
- --width 295
209
- ```
210
-
211
- #### 2. Add Background Color
212
-
213
- ```bash
214
- python requests_api.py \
215
- -u http://127.0.0.1:8080 \
216
- -t add_background \
217
- -i ./idphoto.png \
218
- -o ./idphoto_with_background.jpg \
219
- -c 638cce \
220
- -k 50
221
- ```
222
-
223
- #### 3. Generate 6-inch Layout Photo
224
-
225
- ```bash
226
- python requests_api.py \
227
- -u http://127.0.0.1:8080 \
228
- -t generate_layout_photos \
229
- -i ./idphoto_with_background.jpg \
230
- -o ./layout_photo.jpg \
231
- --height 413 \
232
- --width 295 \
233
- -k 200
234
- ```
235
-
236
- #### Request Failure Scenarios
237
-
238
- - The request fails if more than one face is detected in the photo.
239
-
240
- ## Java Request Example
241
-
242
- ### Add Maven Dependency
243
-
244
- ```java
245
- <dependency>
246
- <groupId>cn.hutool</groupId>
247
- <artifactId>hutool-all</artifactId>
248
- <version>5.8.16</version>
249
- </dependency>
250
-
251
- <dependency>
252
- <groupId>commons-io</groupId>
253
- <artifactId>commons-io</artifactId>
254
- <version>2.6</version>
255
- </dependency>
256
- ```
257
-
258
- ### Running the Code
259
-
260
- #### 1. Generate ID Photo (Transparent Background)
261
-
262
- ```java
263
- /**
264
- * Generate ID Photo (Transparent Background) /idphoto interface
265
- * @param inputImageDir File address
266
- * @return
267
- * @throws IOException
268
- */
269
- public static String requestIdPhoto(String inputImageDir) throws IOException {
270
- String url = BASE_URL+"/idphoto";
271
- // Create file object
272
- File inputFile = new File(inputImageDir);
273
- Map<String, Object> paramMap=new HashMap<>();
274
- paramMap.put("input_image",inputFile);
275
- paramMap.put("height","413");
276
- paramMap.put("width","295");
277
- // Contains status, image_base64_standard, and image_base64_hd
278
- return HttpUtil.post(url, paramMap);
279
- }
280
- ```
281
-
282
- #### 2. Add Background Color
283
-
284
- ```java
285
- /**
286
- * Add Background Color /add_background interface
287
- * @param inputImageDir File address
288
- * @return
289
- * @throws IOException
290
- */
291
- public static String requestAddBackground(String inputImageDir) throws IOException {
292
- String url = BASE_URL+"/add_background";
293
- // Create file object
294
- File inputFile = new File(inputImageDir);
295
- Map<String, Object> paramMap=new HashMap<>();
296
- paramMap.put("input_image",inputFile);
297
- paramMap.put("color","638cce");
298
- paramMap.put("kb","200");
299
- // Response is a JSON dictionary containing status and image_base64
300
- return HttpUtil.post(url, paramMap);
301
- }
302
- ```
303
-
304
- #### 3. Generate 6-inch Layout Photo
305
-
306
- ```java
307
- /**
308
- * Generate 6-inch Layout Photo /generate_layout_photos interface
309
- * @param inputImageDir File address
310
- * @return
311
- * @throws IOException
312
- */
313
- public static String requestGenerateLayoutPhotos(String inputImageDir) throws IOException {
314
- String url = BASE_URL+"/generate_layout_photos";
315
- // Create file object
316
- File inputFile = new File(inputImageDir);
317
- Map<String, Object> paramMap=new HashMap<>();
318
- paramMap.put("input_image",inputFile);
319
- paramMap.put("height","413");
320
- paramMap.put("width","295");
321
- paramMap.put("kb","200");
322
- // Response is a JSON dictionary containing status and image_base64
323
- return HttpUtil.post(url, paramMap);
324
- }
325
- ```
326
-
327
- #### 4. Summary
328
-
329
- ```java
330
-
331
- import cn.hutool.http.HttpUtil;
332
- import cn.hutool.json.JSONObject;
333
- import cn.hutool.json.JSONUtil;
334
- import org.apache.commons.io.FileUtils;
335
- import org.springframework.util.StringUtils;
336
- import java.io.File;
337
- import java.io.IOException;
338
- import java.util.Base64;
339
- import java.util.HashMap;
340
- import java.util.Map;
341
-
342
- /**
343
- * @author: qingshuang
344
- * @createDate: 2024/09/05
345
- * @description: Java generate ID photo, test case
346
- */
347
- public class Test {
348
- /**
349
- * Interface address
350
- */
351
- private final static String BASE_URL = "http://127.0.0.1:8080";
352
-
353
- /**
354
- * Generate ID Photo (Transparent Background) /idphoto interface
355
- * @param inputImageDir File address
356
- * @return
357
- * @throws IOException
358
- */
359
- public static String requestIdPhoto(String inputImageDir) throws IOException {
360
- String url = BASE_URL+"/idphoto";
361
- // Create file object
362
- File inputFile = new File(inputImageDir);
363
- Map<String, Object> paramMap=new HashMap<>();
364
- paramMap.put("input_image",inputFile);
365
- paramMap.put("height","413");
366
- paramMap.put("width","295");
367
- return HttpUtil.post(url, paramMap);
368
- }
369
- /**
370
- * Add Background Color /add_background interface
371
- * @param inputImageDir File address
372
- * @return
373
- * @throws IOException
374
- */
375
- public static String requestAddBackground(String inputImageDir) throws IOException {
376
- String url = BASE_URL+"/add_background";
377
- // Create file object
378
- File inputFile = new File(inputImageDir);
379
- Map<String, Object> paramMap=new HashMap<>();
380
- paramMap.put("input_image",inputFile);
381
- paramMap.put("color","638cce");
382
- paramMap.put("kb","200");
383
- return HttpUtil.post(url, paramMap);
384
- }
385
- /**
386
- * Generate 6-inch Layout Photo /generate_layout_photos interface
387
- * @param inputImageDir File address
388
- * @return
389
- * @throws IOException
390
- */
391
- public static String requestGenerateLayoutPhotos(String inputImageDir) throws IOException {
392
- String url = BASE_URL+"/generate_layout_photos";
393
- // Create file object
394
- File inputFile = new File(inputImageDir);
395
- Map<String, Object> paramMap=new HashMap<>();
396
- paramMap.put("input_image",inputFile);
397
- paramMap.put("height","413");
398
- paramMap.put("width","295");
399
- paramMap.put("kb","200");
400
- return HttpUtil.post(url, paramMap);
401
- }
402
- /**
403
- * Generate ID Photo (Transparent Background)
404
- * @param inputImageDir Source file address
405
- * @param outputImageDir Output file address
406
- * @throws IOException
407
- */
408
- private static void requestIdPhotoToImage(String inputImageDir, String outputImageDir) throws IOException {
409
- String res =requestIdPhoto(inputImageDir);
410
- // Convert to JSON
411
- JSONObject response= JSONUtil.parseObj(res);
412
- if(response.getBool("status")){// Request interface success
413
- String image_base64_standard= response.getStr("image_base64_standard");
414
- String image_base64_hd =response.getStr("image_base64_hd");
415
- String[] outputImageDirArr= StringUtils.split(outputImageDir,".");
416
- // Base64 save as image
417
- FileUtils.writeByteArrayToFile(new File(outputImageDirArr[0]+"_standard."+outputImageDirArr[1]), Base64.getDecoder().decode(image_base64_standard));
418
- FileUtils.writeByteArrayToFile(new File(outputImageDirArr[0]+"_hd."+outputImageDirArr[1]), Base64.getDecoder().decode(image_base64_hd));
419
- }
420
- }
421
- /**
422
- * Add Background Color
423
- * @param inputImageDir Source file address
424
- * @param outputImageDir Output file address
425
- * @throws IOException
426
- */
427
- private static void requestAddBackgroundToImage(String inputImageDir, String outputImageDir) throws IOException {
428
- String res =requestAddBackground(inputImageDir);
429
- // Convert to JSON
430
- JSONObject response= JSONUtil.parseObj(res);
431
- if(response.getBool("status")){// Request interface success
432
- String image_base64= response.getStr("image_base64");
433
- String[] outputImageDirArr= StringUtils.split(outputImageDir,".");
434
- // Base64 save as image
435
- FileUtils.writeByteArrayToFile(new File(outputImageDirArr[0]+"_background."+outputImageDirArr[1]), Base64.getDecoder().decode(image_base64));
436
- }
437
- }
438
- /**
439
- * Generate 6-inch Layout Photo
440
- * @param inputImageDir Source file address
441
- * @param outputImageDir Output file address
442
- * @throws IOException
443
- */
444
- private static void requestGenerateLayoutPhotosToImage(String inputImageDir, String outputImageDir) throws IOException {
445
- String res =requestGenerateLayoutPhotos(inputImageDir);
446
- // Convert to JSON
447
- JSONObject response= JSONUtil.parseObj(res);
448
- if(response.getBool("status")){// Request interface success
449
- String image_base64= response.getStr("image_base64");
450
- String[] outputImageDirArr= StringUtils.split(outputImageDir,".");
451
- // Base64 save as image
452
- FileUtils.writeByteArrayToFile(new File(outputImageDirArr[0]+"_layout."+outputImageDirArr[1]), Base64.getDecoder().decode(image_base64));
453
- }
454
- }
455
-
456
- public static void main(String[] args) {
457
- try {
458
- // Generate ID Photo (Transparent Background)
459
- requestIdPhotoToImage("C:\\Users\\Administrator\\Desktop\\1111.jpg","C:\\Users\\Administrator\\Desktop\\2222.png");
460
- // Add Background Color
461
- requestAddBackgroundToImage("C:\\Users\\Administrator\\Desktop\\2222_hd.png","C:\\Users\\Administrator\\Desktop\\idphoto_with_background.jpg");
462
- // Generate 6-inch Layout Photo
463
- requestGenerateLayoutPhotosToImage("C:\\Users\\Administrator\\Desktop\\1111.jpg","C:\\Users\\Administrator\\Desktop\\2222.png");
464
-
465
- } catch (IOException e) {
466
- e.printStackTrace();
467
- }
468
- }
469
- }
470
- ```
471
-
472
- ## JavaScript Request Examples
473
-
474
- In JavaScript, we can use the `fetch` API to send HTTP requests. Below are examples of how to call these APIs using JavaScript.
475
-
476
- ### 1. Generate ID Photo (Transparent Background)
477
-
478
- ```javascript
479
- async function generateIdPhoto(inputImagePath, height, width) {
480
- const url = "http://127.0.0.1:8080/idphoto";
481
- const formData = new FormData();
482
- formData.append("input_image", new File([await fetch(inputImagePath).then(res => res.blob())], "test.jpg"));
483
- formData.append("height", height);
484
- formData.append("width", width);
485
-
486
- const response = await fetch(url, {
487
- method: 'POST',
488
- body: formData
489
- });
490
-
491
- const result = await response.json();
492
- console.log(result);
493
- return result;
494
- }
495
-
496
- // Example call
497
- generateIdPhoto("images/test.jpg", 413, 295).then(response => {
498
- console.log(response);
499
- });
500
- ```
501
-
502
- ### 2. Add Background Color
503
-
504
- ```javascript
505
- async function addBackground(inputImagePath, color, kb) {
506
- const url = "http://127.0.0.1:8080/add_background";
507
- const formData = new FormData();
508
- formData.append("input_image", new File([await fetch(inputImagePath).then(res => res.blob())], "test.png"));
509
- formData.append("color", color);
510
- formData.append("kb", kb);
511
-
512
- const response = await fetch(url, {
513
- method: 'POST',
514
- body: formData
515
- });
516
-
517
- const result = await response.json();
518
- console.log(result);
519
- return result;
520
- }
521
-
522
- // Example call
523
- addBackground("test.png", "638cce", 200).then(response => {
524
- console.log(response);
525
- });
526
- ```
527
-
528
- ### 3. Generate Six-Inch Layout Photo
529
-
530
- ```javascript
531
- async function generateLayoutPhotos(inputImagePath, height, width, kb) {
532
- const url = "http://127.0.0.1:8080/generate_layout_photos";
533
- const formData = new FormData();
534
- formData.append("input_image", new File([await fetch(inputImagePath).then(res => res.blob())], "test.jpg"));
535
- formData.append("height", height);
536
- formData.append("width", width);
537
- formData.append("kb", kb);
538
-
539
- const response = await fetch(url, {
540
- method: 'POST',
541
- body: formData
542
- });
543
-
544
- const result = await response.json();
545
- console.log(result);
546
- return result;
547
- }
548
-
549
- // Example call
550
- generateLayoutPhotos("test.jpg", 413, 295, 200).then(response => {
551
- console.log(response);
552
- });
553
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hivision/creator/__init__.py CHANGED
@@ -12,7 +12,7 @@ from typing import Tuple
12
  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
16
  from .photo_adjuster import adjust_photo
17
 
18
 
@@ -41,7 +41,9 @@ class IDCreator:
41
  """
42
  # 处理者
43
  self.matting_handler: ContextHandler = extract_human
44
- self.detection_handler: ContextHandler = detect_face
 
 
45
  # 上下文
46
  self.ctx = None
47
 
 
12
  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, detect_face_face_plusplus
16
  from .photo_adjuster import adjust_photo
17
 
18
 
 
41
  """
42
  # 处理者
43
  self.matting_handler: ContextHandler = extract_human
44
+ self.detection_handler: ContextHandler = detect_face_face_plusplus
45
+ # self.detection_handler: ContextHandler = detect_face_mtcnn
46
+
47
  # 上下文
48
  self.ctx = None
49
 
hivision/creator/face_detector.py CHANGED
@@ -9,15 +9,19 @@ r"""
9
  """
10
  from mtcnnruntime import MTCNN
11
  from .context import Context
12
- from hivision.error import FaceError
 
 
13
  import cv2
 
 
14
 
15
  mtcnn = None
16
 
17
 
18
- def detect_face(ctx: Context, scale: int = 2):
19
  """
20
- 人脸检测处理者,只进行人脸数量的检测
21
  :param ctx: 上下文,此时已获取到原始图和抠图结果,但是我们只需要原始图
22
  :param scale: 最大边长缩放比例,原图:缩放图 = 1:scale
23
  :raise FaceError: 人脸检测错误,多个人脸或者没有人脸
@@ -30,13 +34,91 @@ def detect_face(ctx: Context, scale: int = 2):
30
  (ctx.origin_image.shape[1] // scale, ctx.origin_image.shape[0] // scale),
31
  interpolation=cv2.INTER_AREA,
32
  )
33
- faces, _ = mtcnn.detect(image)
 
34
  if len(faces) != 1:
35
  # 保险措施,如果检测到多个人脸或者没有人脸,用原图再检测一次
36
  faces, _ = mtcnn.detect(ctx.origin_image)
37
  else:
 
38
  for item, param in enumerate(faces[0]):
39
  faces[0][item] = param * 2
40
  if len(faces) != 1:
41
  raise FaceError("Expected 1 face, but got {}".format(len(faces)), len(faces))
42
- ctx.face = (faces[0][0], faces[0][1], faces[0][2], faces[0][3], faces[0][4])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  """
10
  from mtcnnruntime import MTCNN
11
  from .context import Context
12
+ from hivision.error import FaceError, APIError
13
+ from hivision.utils import resize_image_to_kb_base64
14
+ import requests
15
  import cv2
16
+ import os
17
+
18
 
19
  mtcnn = None
20
 
21
 
22
+ def detect_face_mtcnn(ctx: Context, scale: int = 2):
23
  """
24
+ 基于MTCNN模型的人脸检测处理器,只进行人脸数量的检测
25
  :param ctx: 上下文,此时已获取到原始图和抠图结果,但是我们只需要原始图
26
  :param scale: 最大边长缩放比例,原图:缩放图 = 1:scale
27
  :raise FaceError: 人脸检测错误,多个人脸或者没有人脸
 
34
  (ctx.origin_image.shape[1] // scale, ctx.origin_image.shape[0] // scale),
35
  interpolation=cv2.INTER_AREA,
36
  )
37
+ faces, _ = mtcnn.detect(image, thresholds=[0.8, 0.8, 0.8])
38
+ # print(len(faces))
39
  if len(faces) != 1:
40
  # 保险措施,如果检测到多个人脸或者没有人脸,用原图再检测一次
41
  faces, _ = mtcnn.detect(ctx.origin_image)
42
  else:
43
+ # 如果只有一个人脸,将人脸坐标放大
44
  for item, param in enumerate(faces[0]):
45
  faces[0][item] = param * 2
46
  if len(faces) != 1:
47
  raise FaceError("Expected 1 face, but got {}".format(len(faces)), len(faces))
48
+
49
+ left = faces[0][0]
50
+ top = faces[0][1]
51
+ width = faces[0][2] - left + 1
52
+ height = faces[0][3] - top + 1
53
+
54
+ ctx.face = (left, top, width, height)
55
+
56
+
57
+ def detect_face_face_plusplus(ctx: Context):
58
+ """
59
+ 基于Face++ API接口的人脸检测处理器,只进行人脸数量的检测
60
+ :param ctx: 上下文,此时已获取到原始图和抠图结果,但是我们只需要原始图
61
+ :param scale: 最大边长缩放比例,原图:缩放图 = 1:scale
62
+ :raise FaceError: 人脸检测错误,多个人脸或者没有人脸
63
+ """
64
+ url = "https://api-cn.faceplusplus.com/facepp/v3/detect"
65
+ api_key = os.getenv("FACE_PLUS_API_KEY")
66
+ api_secret = os.getenv("FACE_PLUS_API_SECRET")
67
+
68
+ image = ctx.origin_image
69
+ # 将图片转为 base64, 且不大于2MB(Face++ API接口限制)
70
+ image_base64 = resize_image_to_kb_base64(image, 2000, mode="max")
71
+
72
+ files = {
73
+ "api_key": (None, api_key),
74
+ "api_secret": (None, api_secret),
75
+ "image_base64": (None, image_base64),
76
+ }
77
+
78
+ # 发送 POST 请求
79
+ response = requests.post(url, files=files)
80
+
81
+ # 获取响应状态码
82
+ status_code = response.status_code
83
+ response_json = response.json()
84
+
85
+ if status_code == 200:
86
+ face_num = response_json["face_num"]
87
+ if face_num == 1:
88
+ face_rectangle = response_json["faces"][0]["face_rectangle"]
89
+ ctx.face = (
90
+ face_rectangle["left"],
91
+ face_rectangle["top"],
92
+ face_rectangle["width"],
93
+ face_rectangle["height"],
94
+ )
95
+ else:
96
+ raise FaceError(
97
+ "Expected 1 face, but got {}".format(face_num), len(face_num)
98
+ )
99
+
100
+ elif status_code == 401:
101
+ raise APIError(
102
+ f"Face++ Status code {status_code} Authentication error: API key and secret do not match.",
103
+ status_code,
104
+ )
105
+
106
+ elif status_code == 403:
107
+ reason = response_json.get("error_message", "Unknown authorization error.")
108
+ raise APIError(
109
+ f"Authorization error: {reason}",
110
+ status_code,
111
+ )
112
+
113
+ elif status_code == 400:
114
+ error_message = response_json.get("error_message", "Bad request.")
115
+ raise APIError(
116
+ f"Bad request error: {error_message}",
117
+ status_code,
118
+ )
119
+
120
+ elif status_code == 413:
121
+ raise APIError(
122
+ f"Face++ Status code {status_code} Request entity too large: The image exceeds the 2MB limit.",
123
+ status_code,
124
+ )
hivision/creator/photo_adjuster.py CHANGED
@@ -21,7 +21,7 @@ def adjust_photo(ctx: Context):
21
  standard_size = ctx.params.size
22
  params = ctx.params
23
  x, y = face_rect[0], face_rect[1]
24
- w, h = face_rect[2] - x + 1, face_rect[3] - y + 1
25
  height, width = ctx.processing_image.shape[:2]
26
  width_height_ratio = standard_size[0] / standard_size[1]
27
  # Step2. 计算高级参数
 
21
  standard_size = ctx.params.size
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. 计算高级参数
hivision/error.py CHANGED
@@ -19,3 +19,15 @@ class FaceError(Exception):
19
  """
20
  super().__init__(err)
21
  self.face_num = face_num
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  """
20
  super().__init__(err)
21
  self.face_num = face_num
22
+
23
+
24
+ class APIError(Exception):
25
+ def __init__(self, err, status_code):
26
+ """
27
+ API错误
28
+ Args:
29
+ err: 错误描述
30
+ status_code: 告诉此时的错误状态码
31
+ """
32
+ super().__init__(err)
33
+ self.status_code = status_code
hivision/utils.py CHANGED
@@ -74,13 +74,20 @@ def resize_image_to_kb(input_image, output_image_path, target_size_kb):
74
  quality = 1
75
 
76
 
77
- def resize_image_to_kb_base64(input_image, target_size_kb):
 
 
 
 
 
 
78
  """
79
  Resize an image to a target size in KB and return it as a base64 encoded string.
80
  将图像调整大小至目标文件大小(KB)并返回base64编码的字符串。
81
 
82
  :param input_image: Input image as a NumPy array or PIL Image. 输入图像,可以是NumPy数组或PIL图像。
83
  :param target_size_kb: Target size in KB. 目标文件大小(KB)。
 
84
 
85
  :return: Base64 encoded string of the resized image. 调整大小后的图像的base64编码字符串。
86
  """
@@ -109,19 +116,30 @@ def resize_image_to_kb_base64(input_image, target_size_kb):
109
  # Get the size of the image in KB
110
  img_size_kb = len(img_byte_arr.getvalue()) / 1024
111
 
112
- # Check if the image size is within the target size
113
- if img_size_kb <= target_size_kb or quality == 1:
 
 
 
 
114
  # If the image is smaller than the target size, add padding
115
- if img_size_kb < target_size_kb:
116
  padding_size = int(
117
  (target_size_kb * 1024) - len(img_byte_arr.getvalue())
118
  )
119
  padding = b"\x00" * padding_size
120
  img_byte_arr.write(padding)
 
 
 
 
 
 
121
 
122
- # Encode the image data to base64
123
- img_base64 = base64.b64encode(img_byte_arr.getvalue()).decode("utf-8")
124
- return img_base64
 
125
 
126
  # Reduce the quality if the image is still too large
127
  quality -= 5
@@ -130,6 +148,10 @@ def resize_image_to_kb_base64(input_image, target_size_kb):
130
  if quality < 1:
131
  quality = 1
132
 
 
 
 
 
133
 
134
  def numpy_2_base64(img: np.ndarray):
135
  _, buffer = cv2.imencode(".png", img)
 
74
  quality = 1
75
 
76
 
77
+ import numpy as np
78
+ from PIL import Image
79
+ import io
80
+ import base64
81
+
82
+
83
+ def resize_image_to_kb_base64(input_image, target_size_kb, mode="exact"):
84
  """
85
  Resize an image to a target size in KB and return it as a base64 encoded string.
86
  将图像调整大小至目标文件大小(KB)并返回base64编码的字符串。
87
 
88
  :param input_image: Input image as a NumPy array or PIL Image. 输入图像,可以是NumPy数组或PIL图像。
89
  :param target_size_kb: Target size in KB. 目标文件大小(KB)。
90
+ :param mode: Mode of resizing ('exact', 'max', 'min'). 模式:'exact'(精确大小)、'max'(不大于)、'min'(不小于)。
91
 
92
  :return: Base64 encoded string of the resized image. 调整大小后的图像的base64编码字符串。
93
  """
 
116
  # Get the size of the image in KB
117
  img_size_kb = len(img_byte_arr.getvalue()) / 1024
118
 
119
+ # Check based on the mode
120
+ if mode == "exact":
121
+ # If the image size is equal to the target size, we can return it
122
+ if img_size_kb == target_size_kb:
123
+ break
124
+
125
  # If the image is smaller than the target size, add padding
126
+ elif img_size_kb < target_size_kb:
127
  padding_size = int(
128
  (target_size_kb * 1024) - len(img_byte_arr.getvalue())
129
  )
130
  padding = b"\x00" * padding_size
131
  img_byte_arr.write(padding)
132
+ break
133
+
134
+ elif mode == "max":
135
+ # If the image size is within the target size, we can return it
136
+ if img_size_kb <= target_size_kb or quality == 1:
137
+ break
138
 
139
+ elif mode == "min":
140
+ # If the image size is greater than or equal to the target size, we can return it
141
+ if img_size_kb >= target_size_kb:
142
+ break
143
 
144
  # Reduce the quality if the image is still too large
145
  quality -= 5
 
148
  if quality < 1:
149
  quality = 1
150
 
151
+ # Encode the image data to base64
152
+ img_base64 = base64.b64encode(img_byte_arr.getvalue()).decode("utf-8")
153
+ return img_base64
154
+
155
 
156
  def numpy_2_base64(img: np.ndarray):
157
  _, buffer = cv2.imencode(".png", img)
inference.py DELETED
@@ -1,107 +0,0 @@
1
- import os
2
- import cv2
3
- import argparse
4
- import numpy as np
5
- import onnxruntime
6
- from hivision.error import FaceError
7
- from hivision.utils import hex_to_rgb, resize_image_to_kb, add_background
8
- from hivision import IDCreator
9
- from hivision.creator.layout_calculator import (
10
- generate_layout_photo,
11
- generate_layout_image,
12
- )
13
-
14
- parser = argparse.ArgumentParser(description="HivisionIDPhotos 证件照制作推理程序。")
15
-
16
- creator = IDCreator()
17
-
18
- parser.add_argument(
19
- "-t",
20
- "--type",
21
- help="请求 API 的种类,有 idphoto、add_background 和 generate_layout_photos 可选",
22
- default="idphoto",
23
- )
24
- parser.add_argument("-i", "--input_image_dir", help="输入图像路径", required=True)
25
- parser.add_argument("-o", "--output_image_dir", help="保存图像路径", required=True)
26
- parser.add_argument("--height", help="证件照尺寸-高", default=413)
27
- parser.add_argument("--width", help="证件照尺寸-宽", default=295)
28
- parser.add_argument("-c", "--color", help="证件照背景色", default="638cce")
29
- parser.add_argument(
30
- "-k", "--kb", help="输出照片的 KB 值,仅对换底和制作排版照生效", default=None
31
- )
32
-
33
- args = parser.parse_args()
34
-
35
- root_dir = os.path.dirname(os.path.abspath(__file__))
36
-
37
- # 预加载 ONNX 模型
38
- print("正在加载抠图模型...")
39
- # HY_HUMAN_MATTING_WEIGHTS_PATH = os.path.join(
40
- # root_dir, "hivision/creator/weights/hivision_modnet.onnx"
41
- # )
42
- # sess = onnxruntime.InferenceSession(HY_HUMAN_MATTING_WEIGHTS_PATH)
43
-
44
- input_image = cv2.imread(args.input_image_dir, cv2.IMREAD_UNCHANGED)
45
-
46
-
47
- # 如果模式是生成证件照
48
- if args.type == "idphoto":
49
-
50
- # 将字符串转为元组
51
- size = (int(args.height), int(args.width))
52
- try:
53
- result = creator(input_image, size=size)
54
- except FaceError:
55
- print("人脸数量不等于 1,请上传单张人脸的图像。")
56
- else:
57
- # 保存标准照
58
- cv2.imwrite(args.output_image_dir, result.standard)
59
-
60
- # 保存高清照
61
- file_name, file_extension = os.path.splitext(args.output_image_dir)
62
- new_file_name = file_name + "_hd" + file_extension
63
- cv2.imwrite(new_file_name, result.hd)
64
-
65
- # 如果模式是添加背景
66
- elif args.type == "add_background":
67
-
68
- # 将字符串转为元组
69
- color = hex_to_rgb(args.color)
70
- # 将元祖的 0 和 2 号数字交换
71
- color = (color[2], color[1], color[0])
72
-
73
- result_image = add_background(input_image, bgr=color)
74
- result_image = result_image.astype(np.uint8)
75
-
76
- if args.kb:
77
- result_image = cv2.cvtColor(result_image, cv2.COLOR_RGB2BGR)
78
- result_image = resize_image_to_kb(
79
- result_image, args.output_image_dir, int(args.kb)
80
- )
81
- else:
82
- cv2.imwrite(args.output_image_dir, result_image)
83
-
84
- # 如果模式是生成排版照
85
- elif args.type == "generate_layout_photos":
86
-
87
- size = (int(args.height), int(args.width))
88
-
89
- typography_arr, typography_rotate = generate_layout_photo(
90
- input_height=size[0], input_width=size[1]
91
- )
92
-
93
- result_layout_image = generate_layout_image(
94
- input_image,
95
- typography_arr,
96
- typography_rotate,
97
- height=size[0],
98
- width=size[1],
99
- )
100
-
101
- if args.kb:
102
- result_layout_image = cv2.cvtColor(result_layout_image, cv2.COLOR_RGB2BGR)
103
- result_layout_image = resize_image_to_kb(
104
- result_layout_image, args.output_image_dir, int(args.kb)
105
- )
106
- else:
107
- cv2.imwrite(args.output_image_dir, result_layout_image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requests_api.py DELETED
@@ -1,119 +0,0 @@
1
- import requests
2
- import base64
3
- import argparse
4
- import os
5
-
6
-
7
- def base64_save(_base64_image_data, save_path):
8
- # 解码 Base64 数据并保存为 PNG 文件
9
- img_data = base64.b64decode(_base64_image_data)
10
- with open(save_path, "wb") as file:
11
- file.write(img_data)
12
-
13
-
14
- # 读取本地图像文件并转换为Base64编码
15
- def file_2_base64(file_path):
16
- with open(file_path, "rb") as file:
17
- encoded_string = base64.b64encode(file.read()).decode("utf-8")
18
- return encoded_string
19
-
20
-
21
- # 发送请求到 /idphoto 接口
22
- def request_idphoto(file_path, height, width):
23
- files = {"input_image": open(file_path, "rb")}
24
- data = {"height": int(height), "width": int(width)}
25
- response = requests.post(url, files=files, data=data)
26
- return response.json()
27
-
28
-
29
- # 发送请求到 /add_background 接口
30
- def request_add_background(file_path, color, kb=None):
31
- files = {"input_image": open(file_path, "rb")}
32
- data = {"color": str(color), "kb": kb}
33
- response = requests.post(url, files=files, data=data)
34
- return response.json()
35
-
36
-
37
- # 发送请求到 /generate_layout_photos 接口
38
- def request_generate_layout_photos(file_path, height, width, kb=None):
39
- files = {"input_image": open(file_path, "rb")}
40
- data = {"height": height, "width": width, "kb": kb}
41
- response = requests.post(url, files=files, data=data)
42
- return response.json()
43
-
44
-
45
- # 示例调用
46
- if __name__ == "__main__":
47
-
48
- parser = argparse.ArgumentParser(
49
- description="HivisionIDPhotos 证件照制作推理程序。"
50
- )
51
- parser.add_argument(
52
- "-u", "--url", help="API 服务的 URL", default="http://localhost:8080"
53
- )
54
-
55
- parser.add_argument(
56
- "-t",
57
- "--type",
58
- help="请求 API 的种类,有 idphoto、add_background 和 generate_layout_photos 可选",
59
- default="idphoto",
60
- )
61
- parser.add_argument("-i", "--input_image_dir", help="输入图像路径", required=True)
62
- parser.add_argument("-o", "--output_image_dir", help="保存图像路径", required=True)
63
- parser.add_argument("--height", help="证件照尺寸-高", default=413)
64
- parser.add_argument("--width", help="证件照尺寸-宽", default=295)
65
- parser.add_argument("-c", "--color", help="证件照背景色", default="638cce")
66
- parser.add_argument(
67
- "-k", "--kb", help="输出照片的 KB 值,仅对换底和制作排版照生效", default=None
68
- )
69
- args = parser.parse_args()
70
-
71
- url = f"{args.url}/{args.type}" # 替换为实际的接口 URL
72
- # color = hex_to_rgb(args.color)
73
- # color = (color[2], color[1], color[0])
74
-
75
- if args.type == "idphoto":
76
- # 调用 /idphoto 接口
77
- idphoto_response = request_idphoto(
78
- args.input_image_dir, int(args.height), int(args.width)
79
- )
80
-
81
- if idphoto_response["status"]:
82
- # 解码 Base64 数据并保存为 PNG 文件
83
- base64_image_data_standard = idphoto_response["image_base64_standard"]
84
- base64_image_data_standard_hd = idphoto_response["image_base64_hd"]
85
-
86
- file_name, file_extension = os.path.splitext(args.output_image_dir)
87
- # 定义新的文件路径(在原有的文件名后添加"_hd")
88
- new_file_name = file_name + "_hd" + file_extension
89
-
90
- # 解码 Base64 数据并保存为 PNG 文件
91
- base64_save(base64_image_data_standard, args.output_image_dir)
92
- base64_save(base64_image_data_standard_hd, new_file_name)
93
-
94
- print(f"请求{args.type}接口成功,已保存图像。")
95
- else:
96
- print("人脸数量不等于 1,请上传单张人脸的图像。")
97
-
98
- elif args.type == "add_background":
99
- # 调用 /add_background 接口
100
- add_background_response = request_add_background(
101
- args.input_image_dir, args.color, kb=args.kb
102
- )
103
- base64_image_data = add_background_response["image_base64"]
104
- base64_save(base64_image_data, args.output_image_dir)
105
-
106
- print(f"请求{args.type}接口成功,已保存图像。")
107
-
108
- elif args.type == "generate_layout_photos":
109
- # 调用 /generate_layout_photos 接口
110
- generate_layout_response = request_generate_layout_photos(
111
- args.input_image_dir, int(args.height), int(args.width), args.kb
112
- )
113
- base64_image_data = generate_layout_response["image_base64"]
114
- base64_save(base64_image_data, args.output_image_dir)
115
-
116
- print(f"请求{args.type}接口成功,已保存图像。")
117
-
118
- else:
119
- print("不支持的 API 类型,请检查输入参数。")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements-app.txt DELETED
@@ -1,2 +0,0 @@
1
- gradio>=4.43.0
2
- fastapi
 
 
 
requirements.txt CHANGED
@@ -3,4 +3,5 @@ onnxruntime>=1.15.0
3
  numpy<=1.26.4
4
  mtcnn-runtime
5
  gradio>=4.43.0
6
- fastapi
 
 
3
  numpy<=1.26.4
4
  mtcnn-runtime
5
  gradio>=4.43.0
6
+ fastapi
7
+ requests
scripts/build_pypi.py DELETED
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env python
2
- # -*- coding: utf-8 -*-
3
- r"""
4
- @DATE: 2024/9/5 16:56
5
- @File: build_pypi.py
6
- @IDE: pycharm
7
- @Description:
8
- 构建pypi包
9
- """
 
 
 
 
 
 
 
 
 
 
test/create_id_photo.py DELETED
@@ -1,22 +0,0 @@
1
- #!/usr/bin/env python
2
- # -*- coding: utf-8 -*-
3
- r"""
4
- @DATE: 2024/9/5 21:39
5
- @File: create_id_photo.py
6
- @IDE: pycharm
7
- @Description:
8
- 用于测试创建证件照
9
- """
10
- from hivision.creator import IDCreator
11
- import cv2
12
- import os
13
-
14
- now_dir = os.path.dirname(__file__)
15
- image_path = os.path.join(os.path.dirname(now_dir), "app", "images", "test.jpg")
16
- output_dir = os.path.join(now_dir, "temp")
17
-
18
- image = cv2.imread(image_path)
19
- creator = IDCreator()
20
- result = creator(image)
21
- cv2.imwrite(os.path.join(output_dir, "result.png"), result.standard)
22
- cv2.imwrite(os.path.join(output_dir, "result_hd.png"), result.hd)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test/temp/.gitkeep DELETED
@@ -1 +0,0 @@
1
- 存放一些测试临时文件