Spaces:
Running
Running
Commit
·
23cd1cf
1
Parent(s):
3519dcc
update
Browse files- Dockerfile +0 -35
- README.md +1 -325
- app.py +59 -11
- app.spec +0 -44
- assets/demoImage.png +0 -3
- assets/gradio-image.jpeg +0 -0
- assets/hivisionai.ico +0 -0
- deploy_api.py +0 -157
- docker-compose.yml +0 -11
- docs/api_CN.md +0 -554
- docs/api_EN.md +0 -553
- hivision/creator/__init__.py +4 -2
- hivision/creator/face_detector.py +87 -5
- hivision/creator/photo_adjuster.py +1 -1
- hivision/error.py +12 -0
- hivision/utils.py +29 -7
- inference.py +0 -107
- requests_api.py +0 -119
- requirements-app.txt +0 -2
- requirements.txt +2 -1
- scripts/build_pypi.py +0 -9
- test/create_id_photo.py +0 -22
- test/temp/.gitkeep +0 -1
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 |
-
[](https://github.com/xiaolin199912/HivisionIDPhotos)
|
| 19 |
-
[](https://swanhub.co/ZeYiLin/HivisionIDPhotos/demo)
|
| 20 |
-
[](https://zhuanlan.zhihu.com/p/638254028)
|
| 21 |
-
[](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 |
-
- 在线体验: [](https://swanhub.co/ZeYiLin/HivisionIDPhotos/demo)、[](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 |
-
[](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 |
-
|
| 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=
|
| 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="
|
| 359 |
-
value=
|
| 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
|
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 |
-
-F "[email protected]" \
|
| 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 |
-
-F "[email protected]" \
|
| 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 |
-
-F "[email protected]" \
|
| 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 |
-
-F "[email protected]" \
|
| 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
|
| 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 =
|
|
|
|
|
|
|
| 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
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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]
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 113 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
# If the image is smaller than the target size, add padding
|
| 115 |
-
|
| 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 |
-
|
| 123 |
-
|
| 124 |
-
|
|
|
|
| 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 |
-
存放一些测试临时文件
|
|
|
|
|
|